• HL 7.18 杂题整理 随笔


    弹飞绵羊:

    源神推荐的LCT模板题,于是就去学习了一下,学的是Chdy的板子,不好看怪他;

    这个题目,将当前点和他即将弹到的点link起来,如果被弹飞了,那么这条边就不存在。

    查询弹飞的步数,就是查询该点到其所属原树中根节点的路径的size

    其实当时还在疑惑为什么Chdy的题解里没有makeroot的操作;

    根据此题,我们并不需要查询或者更改指定路径x-y的信息。

    也就是说,我们根本不需要换根!

    原来需要换根的split,link,cut操作,我们可以根据题目适当调整一下;

    查询原本需要split,我们直接access(x),splay(x)输出xsize

    连边原本需要link,题目保证了是一棵树,我们直接改x的父亲。

    断边原本需要cut,然而我们确定其父亲的位置,access(x),splay(x)后,x的父亲一定在xx的左子树中(LCT的性质),直接双向断开连接。

    自然少了一些函数(pushdown,makeroot,findroot,split,link,cut

    那么这个题就做完了;也就是因为我这个题好多函数没有打好多函数,导致后面有道LCT有点头大;

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200010;
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int n,m,c[N][2],f[N],s[N],a[N];
    
    inline int get(int x) {
        return c[f[x]][0]==x||c[f[x]][1]==x;
    }
    
    inline void pushup(int x) {
        s[x]=s[c[x][0]]+s[c[x][1]]+1;
    }
    
    inline void rotate(int x) {
        int old=f[x],oldf=f[old],k=c[old][1]==x;
        if(get(old))    c[oldf][c[oldf][1]==old]=x;
        c[old][k]=c[x][k^1];c[x][k^1]=old;
        if(c[old][k])    f[c[old][k]]=old;
        f[old]=x; f[x]=oldf;pushup(old);
    }
    
    inline void splay(int x) {
        while(get(x)) {
            int old=f[x],oldf=f[old];
            if(get(old)) rotate((c[old][0]==x)^(c[oldf][0]==old)?x:old);
            rotate(x);
        }
        pushup(x);
    }
    inline void access(int x) {
        for(int y=0;x;x=f[y=x])
            splay(x), c[x][1]=y, pushup(x);
    }
    
    inline void cut(int x,int y) {
        access(x);splay(x);
        if(y) splay(y),c[y][1]=0,f[x]=0;
        pushup(x);
    }
    
    int main() {
    //    freopen("a.in","r",stdin);
    //    freopen("a.out","w",stdout);
        read(n);
        for(int i=1;i<=n;++i) {
            s[i]=1; read(a[i]);
            if(i+a[i]<=n) f[i]=i+a[i];
        }
        read(m);
        for(int i=1,p,x,y;i<=m;i++) {
            read(p); read(x);
            x+=1;
            if(p==1) {
                access(x); splay(x);
                printf("%d
    ",s[x]);
            }
            else {
                read(y);
                int last=a[x];a[x]=y;
                cut(x,x+last>n?0:x+last);
                if(x+a[x]<=n) f[x]=x+a[x];
            }
        }
        return 0;
    }
    View Code

    部落冲突

    无脑LCT,其实是不会树链剖分+线段树;

    开战cut,停战link,判断联通findroot,另外那些函数写的时候注意一点就好了;

    makeroot(x) 把x变为原树的根,即为换根,findroot(x)寻找x在原树的根,split(x,y)提取出来x,y的路径,splay(x)把x弄到当前splay的根,get(x)判断x是不是当前的根;

    #include<bits/stdc++.h>
    using namespace std;
    const int N=300010;
    template<typename T>inline void read(T &x) {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int top; 
    int c[N][2],f[N],s[N],r[N];
    
    void pushdown(int x) {
        if(r[x]) {
            r[x]=0;
            int t=c[x][0];
            r[c[x][0]=c[x][1]]^=1;
            r[c[x][1]=t]^=1;
        }
    }
    
    inline int get(int x) {
        return c[f[x]][0]==x||c[f[x]][1]==x;
    }
    
    inline void rotate(int x) {
        int old=f[x],oldf=f[old],k=c[old][1]==x;
        if(get(old))    c[oldf][c[oldf][1]==old]=x;
        c[old][k]=c[x][k^1]; c[x][k^1]=old;
        if(c[old][k])    f[c[old][k]]=old;
        f[old]=x; f[x]=oldf;
    }
    
    inline void splay(int x) {
        int y=x;top=0;
        s[++top]=y;
        while(get(y))s[++top]=y=f[y];
        while(top)pushdown(s[top--]);
        while(get(x)) {
            int old=f[x],oldf=f[old];
            if(get(old)) rotate((c[old][0]==x)^(c[oldf][0]==old)?x:old);
            rotate(x);
        }
    }
    
    inline void access(int x) {
        for(int y=0;x;x=f[y=x])
            splay(x),c[x][1]=y;
    }
    
    inline int findroot(int x) {
        access(x); splay(x);
        while(c[x][0])    pushdown(x),x=c[x][0];
        splay(x);
        return x;
    }
    
    void makeroot(int x) {
        access(x);splay(x);
        r[x]^=1;pushdown(x);
    }
    
    inline void link(int x,int y) {
        makeroot(x);
        f[x]=y;
    }
    
    inline void cut(int x,int y) {
        makeroot(x);
        access(y);
        splay(y);
        c[y][0]=0,f[x]=0;
    }
    
    int n,m,id,x[N],y[N];
    char ch[3];
    
    int main() {
    //    freopen("a.in","r",stdin);
    //    freopen("a.out","w",stdout);
        read(n); read(m);
        int a,b,u,v,p=0;
        for(int i=1;i<n;i++) {
            read(a); read(b);
            link(a,b);
        }
        for(int i=1;i<=m;i++) {
            scanf("%s",ch);
            if(ch[0]=='Q') {
                read(u); read(v);
                if(findroot(u)==findroot(v)) {
                    printf("Yes
    ");
                }
                else printf("No
    ");
            }
            else if(ch[0]=='U') {
                read(id);
                link(x[id],y[id]);
            }
            else {
                read(u); read(v);
                x[++p]=u;y[p]=v;
                cut(u,v);
            }
        }
        return 0;
    }
    View Code

    文本生成器

    AC自动机+dp;

    首先我们知道对于正向求解问题是比较困难的,那么我们可以反向思维,求其补集,然后总方案数相减;

    这种思维是比较常用的;

    dp[i][j]表示当前构建字符长度为i,对应trie树上的j节点,从父亲向儿子转移就好了;

    //快速幂记得取模;

    #include<bits/stdc++.h>
    using namespace std;
    const int N=6010;
    const int mod=10007;
    template<typename T>inline void read(T &x) {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int trie[N][26],idx,n,m,cnt[N],fail[N],f[200][N];
    char s[N]; 
    
    inline int power(int a,int b) {
        int ans=1;
        while(b) {
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    
    inline void insert(char *s) {
        int p=0;
        for(int i=0;s[i];i++) {
            int ch=s[i]-'A';
            if(!trie[p][ch]) trie[p][ch]=++idx;
            p=trie[p][ch];
        } 
        cnt[p]|=1;
    }
    
    inline void build() {
        queue<int> q;
        memset(fail,0,sizeof(fail));
        for(int i=0;i<26;i++) if(trie[0][i]) q.push(trie[0][i]);
        while(q.size()) {
            int k=q.front();q.pop();
            for(int i=0;i<26;i++) {
                if(trie[k][i]) {
                    q.push(trie[k][i]);
                    fail[trie[k][i]]=trie[fail[k]][i];
                    cnt[trie[k][i]]|=cnt[trie[fail[k]][i]];
                }
                else trie[k][i]=trie[fail[k]][i];
            }
        }
    }
    
    int main() {
        read(n); read(m);
        for(int i=1;i<=n;i++) {
            scanf("%s",s);
            insert(s);
        }
        build();
        f[0][0]=1;
        for(int T=1;T<=m;T++) {
            for(int i=0;i<=idx;i++) {
                for(int j=0;j<26;j++) {
                    if(!cnt[trie[i][j]])
                    (f[T][trie[i][j]]+=f[T-1][i])%=mod;
                }
            }
        }
        int ans=0;
        for(int i=0;i<=idx;i++)
                ans=(ans+f[m][i])%mod;
        printf("%d
    ",((power(26,m)-ans)%mod+mod)%mod);
    }
    View Code

    单词:

    AC自动机的题;

    不要问我为什么天天写AC自动机,因为天天赌题,赌AC自动机的题,温馨提示:久赌必输;

    这个就是建出fail数,然后求出子数大小,就是答案;

    fail树的父亲节点是其儿子节点的前缀;

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000100;
    template<typename T>inline void read(T &x) {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int tot,n,idx,fail[N],trie[N][26],lin[N],orz[N];
    char s[N];
    long long size[N];
    struct gg {
        int y,next;
    }a[N<<1];
    
    inline void add(int x,int y) {
        a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot;
    }
    
    inline void insert(char *s,int v) {
        int p=0;
        for(int i=0;s[i];i++) {
            int ch=s[i]-'a';
            if(!trie[p][ch]) trie[p][ch]=++idx;
            p=trie[p][ch]; size[p]++;
        }
        orz[v]=p;
    }
    
    
    inline void build() {
        queue<int> q;
        memset(fail,0,sizeof(fail));
        for(int i=0;i<26;i++) if(trie[0][i]) q.push(trie[0][i]);
        while(q.size()) {
            int k=q.front(); q.pop();
            for(int i=0;i<26;i++) {
                if(trie[k][i]) {
                    fail[trie[k][i]]=trie[fail[k]][i];
                    q.push(trie[k][i]);
                }
                else trie[k][i]=trie[fail[k]][i];
            }
        }
    }
    
    inline void dfs(int x) {
        for(int i=lin[x];i;i=a[i].next) {
            int y=a[i].y;
            dfs(y);
            size[x]+=size[y];
        }
    }
    
    int main() {
    //    freopen("a.in","r",stdin);
    //    freopen("a.out","w",stdout);
        read(n);
        for(int i=1;i<=n;i++) {
            scanf("%s",s);
            insert(s,i);
        }
        build();
        for(int i=1;i<=idx;i++) {
            add(fail[i],i);
        }
        dfs(0);
        for(int i=1;i<=n;i++) {
            printf("%lld
    ",size[orz[i]]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    ROS tf 两个常用的函数
    C/C++ assert()函数用法总结
    drand48 等 随机数生成函数
    PF部分代码解读
    Error "Client wants topic A to have B, but our version has C. Dropping connection."
    launch 文件的写法
    Spring七大框架
    web.xml filter配置
    web.xml listener配置
    web.xml加载过程
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11204833.html
Copyright © 2020-2023  润新知