源神推荐的LCT模板题,于是就去学习了一下,学的是Chdy的板子,不好看怪他;
这个题目,将当前点和他即将弹到的点link起来,如果被弹飞了,那么这条边就不存在。
查询弹飞的步数,就是查询该点到其所属原树中根节点的路径的size。
其实当时还在疑惑为什么Chdy的题解里没有makeroot的操作;
根据此题,我们并不需要查询或者更改指定路径x-y的信息。
也就是说,我们根本不需要换根!
原来需要换根的split,link,cut操作,我们可以根据题目适当调整一下;
查询原本需要split,我们直接access(x),splay(x)输出x的size。
连边原本需要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; }
无脑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; }
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); }
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; }