【考試總結】2022-04-13
挖寶
通過路徑長度總和可以計算出來這個 \(x\) 點和 \(a\sim b\) 路徑的垂足
如果這個點不是 \(\rm LCA(a,b)\) 那麼可以求出來 \(x\) 的深度,每個深度開一個線段樹,下標是 \(\rm dfn\) 即可
如果垂足是 \(\rm LCA\) 那麼仍然需要考慮子樹裡面的情況,否則需要計算是不是存在一個向上走的路徑使得長度足夠
我的做法是先對全樹進行重鏈剖分,每個點計算除重兒子子樹外能延伸的最長鏈長度 \(d_x\)
考察距離具體貢獻形式可以發現此時對 \(d_x-dep_x\) 在重鏈上做字首 \(\arg\max\) ,每次查詢暴力跳重鏈,輕鏈暴力查扒掉某一子樹區間內的資訊即可
實現的時候注意所謂 fa[top[x]]
這個點已經查詢過了,所以求重鏈 \(\arg \max\) 時需要再跳一步父親
複雜度是 \(\Theta(q\log^2n)\) 的
Code Display
const int N=1e6+10; int n,Q; vector<int> G[N]; int fa[N],son[N],siz[N],dfn[N],dep[N],ord[N],tim,top[N]; const int M=N*20; int ls[M],rs[M],rt[N],tot; vector<int> nds[N]; inline void insert(int &p,int l,int r,int pos){ if(!p) p=++tot; if(l==r) return ; int mid=(l+r)>>1; if(pos<=mid) insert(ls[p],l,mid,pos); else insert(rs[p],mid+1,r,pos); return ; } inline int query(int p,int l,int r,int st,int ed){ if(ed<st||!p) return -1; if(l==r) return l; int mid=(l+r)>>1,res=-1; if(st<=mid) res=query(ls[p],l,mid,st,ed); if(~res) return res; if(ed>mid) res=query(rs[p],mid+1,r,st,ed); return res; } int val[N],len[N]; inline void dfs1(int x,int fat){ dep[x]=dep[fa[x]=fat]+(siz[x]=1); for(auto t:G[x]) if(t!=fat){ dfs1(t,x); siz[x]+=siz[t]; if(siz[t]>siz[son[x]]) son[x]=t; ckmax(len[x],len[t]+1); } return ; } inline void dfs2(int x,int topf){ ord[dfn[x]=++tim]=x; top[x]=topf; insert(rt[dep[x]],1,n,dfn[x]); if(son[x]) dfs2(son[x],topf); for(auto t:G[x]) if(!dfn[t]) dfs2(t,t),ckmax(val[x],len[t]+1); val[x]-=dep[x]; return ; } inline int get_lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } return dep[x]>dep[y]?y:x; } int argmax[N]; inline int jump(int x,int d){ while(d>dep[x]-dep[top[x]]) d-=dep[x]-dep[fa[top[x]]],x=fa[top[x]]; return nds[top[x]][dep[x]-dep[top[x]]-d]; } inline int get_son(int y,int x){ int lst=-1; while(top[x]!=top[y]) y=fa[lst=top[y]]; if(y==x) return lst; else return son[x]; } signed main(){ n=read(); Q=read(); for(int i=1;i<n;++i){ int u=read(),v=read(); G[u].emplace_back(v); G[v].emplace_back(u); } dfs1(1,0); dfs2(1,1); for(int i=1;i<=n;++i) if(top[i]==i){ nds[i].emplace_back(i); int x=son[i],lst=i; argmax[i]=i; while(x){ nds[i].emplace_back(x); if(val[x]>val[argmax[lst]]) argmax[x]=x; else argmax[x]=argmax[lst]; x=son[lst=x]; } } while(Q--){ int a=read(),da=read(),b=read(),db=read(); int lca=get_lca(a,b),dis=dep[a]+dep[b]-2*dep[lca]; if(da+db<dis||(da+db-dis)%2){ puts("-1"); continue; } if(dep[a]>dep[b]) swap(a,b),swap(da,db); int waste=(da+db-dis)/2; if(da<waste||db<waste){puts("-1"); continue;} int D=da-waste; int x=0; if(dep[a]-dep[lca]>=D) x=jump(a,D); else{ D-=dep[a]-dep[lca]; D=dep[b]-dep[lca]-D; x=jump(b,D); } if(x==lca){ if(dep[x]>waste){ print(jump(x,waste)); continue; } int t1=get_son(b,x); if(a==lca){ int tar=dep[x]+waste; int ans=-1; if(~(ans=query(rt[tar],1,n,dfn[lca],dfn[t1]-1))){ print(ord[ans]); continue; } ans=query(rt[tar],1,n,dfn[t1]+siz[t1],dfn[x]+siz[x]-1); if(~ans){ print(ord[ans]); continue; } }else{ int t2=get_son(a,x),tar=dep[x]+waste; if(dfn[t1]>dfn[t2]) swap(t1,t2); int ans=query(rt[tar],1,n,dfn[x],dfn[t1]-1); if(~ans){ print(ord[ans]); continue; } ans=query(rt[tar],1,n,dfn[t1]+siz[t1],dfn[t2]-1); if(~ans){ print(ord[ans]); continue; } ans=query(rt[tar],1,n,dfn[t2]+siz[t2],dfn[x]+siz[x]-1); if(~ans){ print(ord[ans]); continue; } } int ori=x,lst=x; while(x){ if(x==lst){ if(x!=top[x]) x=fa[x]; else goto Tag; } if(dep[ori]+val[argmax[x]]>=waste){ x=argmax[x]; int tar=waste-(dep[ori]-dep[x])+dep[x]; print(ord[query(rt[tar],1,n,dfn[son[x]]+siz[son[x]],dfn[x]+siz[x]-1)]); goto Here; } Tag:; int pre=top[x]; lst=x=fa[top[x]]; int tar=waste-(dep[ori]-dep[x])+dep[x]; int ans=query(rt[tar],1,n,dfn[x],dfn[pre]-1); if(~ans){ print(ord[ans]); goto Here; } ans=query(rt[tar],1,n,dfn[pre]+siz[pre],dfn[x]+siz[x]-1); if(~ans){ print(ord[ans]); goto Here; } } puts("-1"); Here:; }else{ int tson=0; if(x==a||x==b) tson=0; else if(get_lca(x,a)==x) tson=get_son(a,x); else tson=get_son(b,x); int tar=dep[x]+waste; if(tson){ int ans=query(rt[tar],1,n,dfn[x],dfn[tson]-1); if(~ans){ print(ord[ans]); continue; } ans=query(rt[tar],1,n,dfn[tson]+siz[tson],dfn[x]+siz[x]-1); if(~ans){ print(ord[ans]); continue; } }else{ int ans=query(rt[tar],1,n,dfn[x],dfn[x]+siz[x]-1); if(~ans){ print(ord[ans]); continue; } } print(-1); } } return 0; }
圖
使用最小表示法記錄最後 \(12\) 個點屬於哪些連通塊的做法可以通過,並且執行速度出奇快
標算考慮對 \(\bmod\ 2\) 做文章:如果某種選點方案出現了 \(t\) 個連通塊,如果使之造成 \(2^{t-1}\) 的貢獻就可以將不合法的情況消去了
那麼轉變成求 \(2^t\) 模 \(4\) 的餘數再除二即可
此時變成出現了 \(t\) 個連通塊就造成 \(2^t\) 的貢獻,這個可以通過黑白染色來刻畫:每個點有 \(3\) 個狀態 \(0/1/2\) ,每個邊的兩個端點狀態全不為 \(2\) 就必須滿足狀態一樣
使用 \(3\) 進位制狀壓可以解決這個問題,複雜度是合理的 \(\Theta(n3^12)\)
Code Display
int n,m,Bas;
vector<int> lar[100],G[100];
set<int> dp[100];
inline int encode(vector<int> sta){
int S=0;
for(int i=0;i<Bas;++i) S=S*(1+Bas)+sta[i];
return S;
}
inline vector<int> decode(int S){
vector<int> res;
for(int i=Bas-1;~i;--i) res.emplace_back(S%(1+Bas)),S/=(1+Bas);
reverse(res.begin(),res.end());
return res;
}
struct DSU{
int fa[100];
inline void init(int l,int r){rep(i,l,r) fa[i]=i; return ;}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){fa[find(x)]=find(y); return ;}
}Dsu;
int id[100];
signed main(){
n=read(); m=read();
for(int i=1;i<=m;++i){
int u=read(),v=read();
if(u<v) swap(u,v);
G[u].emplace_back(v);
lar[v].emplace_back(u);
ckmax(Bas,u-v);
}
if(m==0) print(n&1),exit(0);
if(n<18){
int S=1<<n,ans=0;
vector<bool> vis(n+1),mark(n+1);
for(int i=1;i<S;++i){
vector<int> nds;
rep(j,1,n) if(i>>(j-1)&1) mark[j]=1,vis[j]=0,nds.emplace_back(j);
function<void(int)> dfs=[&](const int x){
if(vis[x]) return ; vis[x]=1;
for(auto t:G[x]) if(mark[t]) dfs(t);
for(auto t:lar[x]) if(mark[t]) dfs(t);
};
dfs(nds.back());
bool dlt=1;
rep(j,1,n) if(i>>(j-1)&1){
dlt&=vis[j];
vis[j]=0;
mark[j]=0;
}
ans^=dlt;
}
print(ans);
exit(0);
}
int S=1<<Bas,ans=0;
for(int i=0;i<S;++i){
Dsu.init(1,Bas);
for(int j=1;j<=Bas;++j) if(i>>(j-1)&1){
for(auto t:G[j]) if(i>>(t-1)&1) Dsu.merge(j,t);
}
vector<int> sta(Bas);
int num=0;
for(int j=1;j<=Bas;++j) if(i>>(j-1)&1){
if(!id[Dsu.find(j)]) id[Dsu.find(j)]=++num;
sta[j-1]=id[Dsu.find(j)];
}
rep(j,1,Bas) id[j]=0;
dp[Bas].insert(encode(sta));
}
int only=1;
for(int i=1;i<=Bas-1;++i) only*=Bas+1;
for(int i=Bas;i<n;++i){
for(auto sta:dp[i]){
vector<int> vec=decode(sta);
auto get_num=[&](vector<int> &cur){
int cnt=0;
for(int j=1;j<Bas;++j) cnt+=cur[j]==cur[0];
return cnt;
};
int gnum=get_num(vec);
if(vec[0]==0||gnum){
vector<int> nxt(Bas);
int num=0;
rep(j,1,Bas-1) if(vec[j]){
if(!id[vec[j]]) id[vec[j]]=++num;
nxt[j-1]=id[vec[j]];
}
rep(j,1,Bas-1) if(vec[j]) id[vec[j]]=0;
int tmp=encode(nxt);
if(dp[i+1].count(tmp)) dp[i+1].erase(tmp);
else dp[i+1].insert(tmp);
}
if(sta==only) ans^=1;
Dsu.init(i-Bas+1,i+1);
vector<int> lst(Bas+1,-1);
int num=0;
rep(j,0,Bas-1) if(vec[j]){
int cur=i-Bas+1+j;
if(~lst[vec[j]]) Dsu.merge(cur,lst[vec[j]]);
lst[vec[j]]=cur;
}
bool linked=0;
for(auto t:G[i+1]) if(vec[Bas-(i+1-t)]!=0){
Dsu.merge(i+1,t);
linked|=t==i-Bas+1;
}
if(vec[0]==0||linked||gnum){
vector<int> nxt(Bas);
int num=0;
rep(j,1,Bas) if(j==Bas||vec[j]){
int cur=i+1-Bas+j;
if(!id[Dsu.find(cur)]) id[Dsu.find(cur)]=++num;
nxt[j-1]=id[Dsu.find(cur)];
}
rep(j,1,Bas) if(j==Bas||vec[j]) id[Dsu.find(i+1-Bas+j)]=0;
int tmp=encode(nxt);
if(dp[i+1].count(tmp)) dp[i+1].erase(tmp);
else dp[i+1].insert(tmp);
}
}
}
if(dp[n].count(0)) dp[n].erase(0);
else dp[n].insert(0);
for(auto t:dp[n]){
vector<int> vec=decode(t);
for(auto v:vec) if(v>1) goto Here;
ans^=1;
Here:;
}
print(ans);
return 0;
}
字串
考慮將相似刻畫為 \(\rm LCP+LCS\ge m-1\) ,所以建立正反兩個字尾自動機讓它變成一道 \(\text{Data Structure}\) 題
在其中一棵樹 \(T_1\) 上 \(\text{Dsu On Tree}\),並在過程中維護一個點集
將某個點 \(x\) 加入點集時首先計算點集中的點對其造成的貢獻,\(x\) 在 \(T_2\) 上的對應點 \(y\) ,那麼找到 \(len_x+len_{y\to anc}\ge m-1\) 的最淺 \(anc\) 並求當前點集在 \(T_2\) 中的對應點有幾個在 \(anc\) 的子樹裡面
子樹資訊和使用 \(\rm Fenwick\ Tree\) 即可維護
而它對點集中現在 在 \(anc\) 子樹裡面的點的答案造成 \(1\) 的貢獻,這個問題是子樹加單點查詢
另外維護一個樹狀陣列,在加入點集時減去對 \(\rm dfn\) 單點查詢的資訊,從點集中刪掉時加上對 \(\rm dfn\) 單點查詢的資訊就可以查詢這個點在點集中時有多少點對其造成了貢獻
實現的時候注意 加刪點/查詢增量 的順序,即輕兒子統一加入點集時要先在 \(T_2\) 加入再刪版本資訊
Code Display
const int N=2e5+10;
int n,m,ans[N];
char s[N];
struct Suffix_Automation{
vector<int> G[N];
int len[N],fa[N],son[N][26],tot=1,las=1,pos[N];
int mark[N];
inline void extend(int x,int id){
int tmp=las,np=las=++tot; len[np]=len[tmp]+1;
pos[mark[np]=len[np ]]=np;
while(!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
if(!tmp) return fa[np]=1,void();
int q=son[tmp][x];
if(len[q]==len[tmp]+1) return fa[np]=q,void();
int clone=++tot;
len[clone]=len[tmp]+1; fa[clone]=fa[q]; fa[q]=fa[np]=clone;
for(int i=0;i<26;++i) son[clone][i]=son[q][i];
while(son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];
return ;
}
inline void init(){
for(int i=2;i<=tot;++i) G[fa[i]].emplace_back(i);
return ;
}
}Pre,Suf;
int bz[N][20],in[N],out[N],tim;
inline void dfs1(int x,int fat){
bz[x][0]=fat;
in[x]=++tim;
for(int i=1;bz[x][i-1];++i) bz[x][i]=bz[bz[x][i-1]][i-1];
for(auto t:Pre.G[x]) dfs1(t,x);
out[x]=tim;
return ;
}
inline int get_anc(int x,int tar){
for(int i=19;~i;--i) if(bz[x][i]&&Pre.len[bz[x][i]]>=tar) x=bz[x][i];
return x;
}
struct Fenwick_Tree{
int c[N];
inline void insert(int x,int v){
for(;x<=tim;x+=x&(-x)) c[x]+=v;
}
inline int query(int x){
int res=0;
for(;x;x-=x&(-x)) res+=c[x];
return res;
}
}T1,T2;
int son[N],siz[N];
inline void get_son(int x){
siz[x]=(bool)Suf.mark[x];
for(auto t:Suf.G[x]){
get_son(t); siz[x]+=siz[t];
if(siz[t]>siz[son[x]]) son[x]=t;
}
return ;
}
vector<int> sub[N];
int mp[N],anc[N];
auto ins_node=[](const int x,const int coef){
T2.insert(in[anc[x]],coef);
T2.insert(out[anc[x]]+1,-coef);
return ;
};
auto query=[](const int x){
return T1.query(out[x])-T1.query(in[x]-1);
};
inline void solve(int x,int opt){
for(auto t:Suf.G[x]) if(t!=son[x]) solve(t,0);
if(son[x]) solve(son[x],1),swap(sub[x],sub[son[x]]);
if(Suf.mark[x]>=m){
anc[x]=get_anc(mp[x],m-1-Suf.mark[x]);
ans[Suf.mark[x]]+=query(anc[x]);
ins_node(x,1);
T1.insert(in[mp[x]],1);
ans[Suf.mark[x]]-=T2.query(in[mp[x]]);
sub[x].emplace_back(x);
// Insert the node x itself
}
for(auto t:Suf.G[x]) if(t!=son[x]){
for(auto node:sub[t]){
anc[node]=get_anc(mp[node],m-1-Suf.len[x]);
ans[Suf.mark[node]]+=query(anc[node]);
ins_node(node,1);
}
for(auto node:sub[t]){
T1.insert(in[mp[node]],1);
ans[Suf.mark[node]]-=T2.query(in[mp[node]]);
sub[x].emplace_back(node);
}
}// Insert the node from the subtree of x's ligth sons
if(!opt){
for(auto node:sub[x]) ans[Suf.mark[node]]+=T2.query(in[mp[node]]);
for(auto node:sub[x]){
ins_node(node,-1);
T1.insert(in[mp[node]],-1);
}
// Remove the subtree of x from the nodes set
}
return ;
}
signed main(){
n=read(); m=read(); scanf("%s",s+1);
for(int i=n;i;--i) Pre.extend(s[i]-'a',i);
Pre.init();
dfs1(1,0);
for(int i=1;i<=n;++i) Suf.extend(s[i]-'a',i);
Suf.init();
for(int i=1;i<=Suf.tot;++i) if(Suf.mark[i]>=m) mp[i]=Pre.pos[n+m-Suf.mark[i]];
get_son(1);
solve(1,0);
rep(i,m,n) print(ans[i]); putchar('\n');
return 0;
}