可持久化作業(上)

全序開題
準備打假

A.CF757G Can Bash Save the Day?

樹上動態求pl...prp_l...p_rxx的距離和,支持交換px,px+1p_x,p_{x+1}
可持久化點分樹,答案爲第rr個版本上點分樹的答案減去第l1l-1個版本上點分樹的答案,修改就是對第xx個版本的單點修改。
因爲可持久化需要把到兒子的鏈接全部複製(如果你寫自底向上的點分樹也一樣需要把兒子到父親的鏈接全部修改),所以兒子個數太多可持久化複雜度就沒有保證,需要三度化原圖後再進行點分治。
UPD:仔細想了一想,如果要維護fafa的可持久化數組的話,那麼一次修改好像需要改整個子樹,所以自底向上好像根本寫不了的樣子,就照可持久化線段樹的寫法寫吧。

AC Code\mathcal AC \ Code

//11:06~12:12
//Can Bush Save the Day ? 
#include<bits/stdc++.h>
#define maxn 400005
#define maxp maxn * 20
#define lim 30
#define LL long long
#define pb push_back
#define pii pair<int,LL>
#define mp make_pair
#define Fi first
#define Se second
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

const int M = (1 << 30) - 1;
int n,q,a[maxn],cnt_p;
int sz[maxn],dep[maxn],rt[maxn],st[maxn],ed[maxn],id[maxp],tot;
LL ds[maxn][lim],Sm[maxp],Sz[maxp],Smf[maxp],Szf[maxp];
vector<pii >tE[maxn],E[maxn];
vector<int>G[maxp];

void dfs0(int u,int ff){
	int p=0;
	for(auto v:tE[u]) if(v.Fi^ff){
		if(!p){
			E[u].pb(mp(v.Fi,v.Se));
			E[v.Fi].pb(mp(u,v.Se));
			p = u;
		}
		else{
			int t = ++cnt_p;
			E[p].pb(mp(t,0));
			E[t].pb(mp(p,0));
			
			E[t].pb(mp(v.Fi,v.Se));
			E[v.Fi].pb(mp(t,v.Se));
			p = t;
		}
		dfs0(v.Fi,u);
	}
}

void dfs1(int u,int ff,int tsz,int &mn,int &rt){
	int mx = 0;sz[u] = 1;
	for(auto v:E[u]) if(v.Fi^ff && !dep[v.Fi])
		dfs1(v.Fi,u,tsz,mn,rt),sz[u]+=sz[v.Fi],mx=max(mx,sz[v.Fi]);
	if((mx=max(mx,tsz-sz[u])) < mn)
		mn = mx , rt = u;
}

int Gert(int u,int tsz){
	int mn,rt;
	dfs1(u,0,tsz,mn=0x3f3f3f3f,rt=-1);
	return rt;
}

void dfs2(int u,int ff,LL dis,int d){
	ds[u][d] = dis;sz[u] = 1;
	for(auto v:E[u]) if(v.Fi^ff && !dep[v.Fi])
		dfs2(v.Fi,u,dis+v.Se,d),sz[u]+=sz[v.Fi];
}

void Build(int u,int d){
	dep[u] = d;
	dfs2(u,0,0,d);
	for(auto v:E[u]) if(!dep[v.Fi]){
		int t = Gert(v.Fi,sz[v.Fi]);
		G[u].pb(t);
		Build(t,d+1);	
	}
}

void dfs3(int u){
	static int tot = 0;
	st[u] = ++tot;
	for(int v:G[u]) dfs3(v);
	ed[u] = tot;
}

void ins(int &u,int p,int d){
	Sm[++tot] = Sm[u] + ds[p][d] , Sz[tot] = Sz[u] + 1 , 
	Smf[tot] = Smf[u] + ds[p][d-1] , Szf[tot] = Szf[u] + 1;
	G[tot] = G[u] , id[tot] = id[u];
	u = tot;
	for(int &v:G[u]) if(st[id[v]] <= st[p] && st[p] <= ed[id[v]])
		ins(v,p,d+1);
}

LL qry(int u,int p,int d){
	LL r = Sm[u] + Sz[u] * ds[p][d];
	if(d>1) r -= Smf[u] + Szf[u] * ds[p][d-1];
	for(int v:G[u]) if(st[id[v]] <= st[p] && st[p] <= ed[id[v]])
		r += qry(v,p,d+1);
	return r;
}

int main(){
	
//	freopen("1.in","r",stdin);
	
	read(n),read(q);
	rep(i,1,n) read(a[i]);
	rep(i,1,n-1){
		int u,v,w;
		read(u),read(v),read(w);
		tE[u].pb(mp(v,w)),tE[v].pb(mp(u,w));
	}
	cnt_p = n;
	dfs0(1,0);
	Build(rt[0]=Gert(1,cnt_p),1);
	dfs3(rt[0]);
	rep(i,1,cnt_p) id[i] = i;tot=cnt_p;
	rep(i,1,n) ins(rt[i]=rt[i-1],a[i],1);
	LL ans = 0;
	for(int t,a,b,c;q--;){
		read(t);
		if(t == 1){
			read(a),read(b),read(c);
			int l = (ans & M) ^ a , r = (ans & M) ^ b , v = (ans & M) ^ c;
			ans = qry(rt[r],v,1) - qry(rt[l-1],v,1);
			printf("%lld\n",ans);
		}
		else{
			read(a);
			int x = (ans & M) ^ a;
			ins(rt[x]=rt[x-1],::a[x+1],1);
			swap(::a[x],::a[x+1]);
		}
	}
}

CF650D Zip-line

每次詢問將原序列的一個點的值改變後的最長嚴格上升子序列的長度。
那麼修改後的最長上升子序列分爲經過修改點和不經過的。
不經過的可以求出每個aa,最靠前的bb使得arra>arrbarr_a > arr_b,且bb結尾的最長上升子序列長度與aa開頭的最長上升子序列長度之和爲最長上升子序列長度。
然後對於[a+1,b1][a+1,b-1]區間取maxmax即可,可以用stst表實現。
經過的可以用可持久化線段樹簡單維護。
可能對於不經過的有更優的其他做法。
UPD:不經過的可以通過判斷是否每個LIS都經過這個點來得知答案是LISLIS還是LIS1LIS-1,可以轉化爲:
假設這個點xx結尾的LISLIS長度是aa,那麼就是判斷是否對於所有可能在全局LISLIS中的點y(yx)y(y\neq x),以yy結尾的LISLIS長度是否是aa,如果是,則有LISLIS不經過xx

AC Code\mathcal AC \ Code

//12:20~12:52
//Zip-Line
#include<bits/stdc++.h>
#define maxn 400005
#define maxp maxn * 50
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char start;

int n,m,a[maxn],b[maxn];
int h[maxn],sb[maxn<<1],rt[2][maxn],f[2][maxn],lc[maxp],rc[maxp],mx[maxp],in[maxn],tot;
vector<int>G[maxn];
void ins(int &u,int l,int r,int p,int v){
	lc[++tot] = lc[u] , rc[tot] = rc[u] , mx[tot] = max(mx[u] , v);
	u = tot;
	if(l == r) return;
	int m = l+r >> 1;
	p <= m ? ins(lc[u],l,m,p,v) : ins(rc[u],m+1,r,p,v);
}
int qry(int u,int l,int r,int ql,int qr){
	if(l>qr||ql>r||ql>qr||!u) return 0;
	if(ql<=l&&r<=qr) return mx[u];
	int m=l+r>>1;
	return max(qry(lc[u],l,m,ql,qr),qry(rc[u],m+1,r,ql,qr));
}

char End;

int main(){
	scanf("%d%d",&n,&m);
	rep(i,1,n)
		scanf("%d",&h[i]),sb[++sb[0]] = h[i];
	rep(i,1,m) 
		scanf("%d%d",&a[i],&b[i]),sb[++sb[0]] = b[i];
	sort(sb+1,sb+1+sb[0]);
	sb[0] = unique(sb+1,sb+1+sb[0])-sb-1;
	rep(i,1,n){
		h[i] = lower_bound(sb+1,sb+1+sb[0],h[i])-sb;
		f[0][i] = qry(rt[0][i-1],1,sb[0],1,h[i]-1) + 1;
		ins(rt[0][i] = rt[0][i-1],1,sb[0],h[i],f[0][i]);
	}
	per(i,n,1){
		f[1][i] = qry(rt[1][i+1],1,sb[0],h[i]+1,sb[0]) + 1;
		ins(rt[1][i] = rt[1][i+1],1,sb[0],h[i],f[1][i]);
	}
	int mx = *max_element(f[0]+1,f[0]+n+1);
	rep(i,1,n) if(f[0][i] + f[1][i] == mx + 1)
		G[f[0][i]].push_back(i);
	rep(i,1,n) if(G[i].size() == 1)
		in[G[i][0]] = 1;
	rep(i,1,m){
		b[i] = lower_bound(sb+1,sb+1+sb[0],b[i])-sb; 
		printf("%d\n",max(qry(rt[0][a[i]-1],1,sb[0],1,b[i]-1)+qry(rt[1][a[i]+1],1,sb[0],b[i]+1,sb[0])+1,mx-in[a[i]]));
	}
}

CF547E Mike and Friends

長度爲nn的字符串序列s1,s2,s3...sns_1,s_2,s_3...s_n
每次詢問一個l,r,kl,r,k,求在sl...srs_l...s_rsks_k在每個字符串中作爲子串出現次數的和。

可持久化廣義後綴自動機
可持久化廣義後綴自動機的failfail樹即可。
對於一個sl...srs_l...s_r,把每個字符串的每個前綴在後綴自動機上的節點都+1+1
求子樹和即爲出現次數。
單點修改子樹查詢。
但是爲什麼不離線呢?
因爲這是可持久化作業。

AC Code\mathcal AC \ Code

//Endless Falling Light.
//14:22~15:08
//Mike and Friends 
#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define maxc 26
#define maxp maxn * 20
using namespace std;

char ST;

char *s[maxn],S[maxn];
int n,q,slen[maxn],pos[maxn],rt[maxn];
int last,tr[maxn<<1][maxc],fa[maxn<<1],len[maxn<<1],tot;
int lc[maxp],rc[maxp],sm[maxp],cnt;

void ins(int c){
	int p,q;
	if(tr[last][c]){
		p = last;
		if(len[q = tr[p][c]] != len[p] + 1){
			int cl = ++tot;
			memcpy(tr[cl],tr[q],sizeof tr[q]);
			fa[cl] = fa[q] , len[cl] = len[p] + 1;
			for(;p!=-1 && tr[p][c] == q;tr[p][c] = cl , p = fa[p]);
			fa[q] = cl;
		}
	}
	else{
		int cur = ++tot;
		len[cur] = len[p = last] + 1;
		for(;p!=-1 && !tr[p][c];tr[p][c]=cur,p=fa[p]);
		if(p == -1) fa[cur] = 0;
		else if(len[q = tr[p][c]] == len[p] + 1) fa[cur] = q;
		else{
			int cl = ++tot;
			memcpy(tr[cl],tr[q],sizeof tr[q]);
			fa[cl] = fa[q] , len[cl] = len[p] + 1;
			for(;p!=-1 && tr[p][c] == q;tr[p][c] = cl , p = fa[p]);
			fa[q] = fa[cur] = cl;
		}
	}
}//last maintained outside

void ins(int &u,int l,int r,int p){
	lc[++cnt] = lc[u] , rc[cnt] = rc[u] , sm[cnt] = sm[u] + 1;
	u = cnt;
	if(l==r) return ;
	int m = l+r>>1;
	p <= m ? ins(lc[u],l,m,p) : ins(rc[u],m+1,r,p);
}
int qry(int u,int l,int r,int ql,int qr){
	if(l>qr||ql>r||ql>qr||!u) return 0;
	if(ql<=l&&r<=qr) return sm[u];
	int m=l+r>>1;
	return qry(lc[u],l,m,ql,qr) + qry(rc[u],m+1,r,ql,qr);
}
vector<int>G[maxn<<1];
int st[maxn<<1],ed[maxn<<1],tim;

void dfs(int u){
	st[u] = ++tim;
	for(int v:G[u])
		dfs(v);
	ed[u] = tim;
}

char ED;

int main(){
	scanf("%d%d",&n,&q);
	fa[0] = -1;
	rep(i,1,n){
		scanf("%s",S);
		slen[i] = strlen(S);
		s[i] = new char[slen[i]+1];
		memcpy(s[i],S,sizeof(char) * slen[i]);
		last = 0;
		rep(j,0,slen[i]-1) ins(s[i][j]-'a'),last=tr[last][s[i][j]-'a'];
	}
	rep(i,1,tot) G[fa[i]].push_back(i);
	dfs(0);
	rep(i,1,n){
		rt[i] = rt[i-1];
		rep(j,0,slen[i]-1)
			pos[i] = tr[pos[i]][s[i][j]-'a'],
			ins(rt[i],1,tot+1,st[pos[i]]);
	}
	for(int l,r,K;q--;){
		scanf("%d%d%d",&l,&r,&K);
		printf("%d\n",qry(rt[r],1,tot+1,st[pos[K]],ed[pos[K]]) - qry(rt[l-1],1,tot+1,st[pos[K]],ed[pos[K]]));
	}
}

THUSC2016補退選

Trie\texttt {Trie}樹上插入刪除,求單點值大小超過vv的最早時刻。
可持久化地維護每個版本的歷史最大和即可nlog2nn\log ^2n二分答案詢問,nlognn\log n插入。
但是這個題可以在Trie\texttt{Trie}上用vector\texttt{vector}維護第一次和爲xx的最小時刻的近乎暴力的方法得到nlognn\log n的複雜度。
那麼爲什麼要可持久化呢
也許這就是可持久化變量吧

AC Code\mathcal AC \ Code

//Waterloo.gugu.com
//15:12 ~ 15:22
//No No Choice
#include<bits/stdc++.h>
#define maxn 100005
#define maxp maxn * 61
#define maxc 10
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

int n;
int tr[maxp][maxc],sz[maxp],tot;
vector<int>G[maxp];
char S[61];

int main(){
	scanf("%d",&n);
	int ans = 0;
	rep(i,1,n){int op;
		scanf("%d",&op);
		if(op == 1){
			scanf("%s",S);
			int L = strlen(S) , u = 0;
			rep(j,0,L-1){
				int v = S[j] - 'a';
				if(!tr[u][v]) tr[u][v] = ++tot;
				u = tr[u][v];
				sz[u] ++;
				if(sz[u] > G[u].size()) G[u].push_back(i);
			}
		}
		if(op == 2){
			scanf("%s",S);
			int L = strlen(S) , u = 0;
			rep(j,0,L-1){
				int v = S[j] - 'a';
				if(!tr[u][v]) tr[u][v] = ++tot;
				u = tr[u][v];
				sz[u] --;
			}
		}
		if(op == 3){
			scanf("%s",S);
			ans = abs(ans);
			int a,b,c;scanf("%d%d%d",&a,&b,&c);
			int t = (1ll * a * ans + b) % c + 1;
			
			int L = strlen(S) , u = 0;
			rep(j,0,L-1){
				int v = S[j] - 'a';
				u = tr[u][v];
				if(!u){
					u = -1;
					break;
				}
			}
			if(u == -1) ans = -1;
			else if(G[u].size() >= t) ans = G[u][t-1];
			else ans = -1;
			printf("%d\n",ans); 
		}
	}
}

LOJ #6198. 謝特

給出一個字符串ss,定義兩個後綴(i,j)(i,j)組合的權值爲LCP(s[i..n],s[j...n])+(wiwj)LCP(s[i..n],s[j...n]) + (w_i \wedge w_j)
求兩個後綴組合權值的最大值。
考慮在後綴數組的heightheight數組上操作。
那麼變成每次取最小值的位置,求這個位置兩邊各選一個ww異或起來的最大值。
直接啓發式合併,掃短的那邊放在長的那邊的trietrie樹上查詢即可。
有理有據的可持久化

$\mathcal AC \ Code $

//shi-----(f)t.
//15:23~16:10
//shit
#include<bits/stdc++.h>
#define maxn 100005
#define lim 17
#define maxp maxn * 30
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

int n,tr[maxp][2],sz[maxp],rt[maxn],tot;
char s[maxn];

void ins(int &u,int w,int d){
	sz[++tot] = sz[u] + 1 , tr[tot][0] = tr[u][0] , tr[tot][1] = tr[u][1];
	u = tot;
	if(d < 0) return;
	int v = w >> d & 1;
	ins(tr[u][v],w,d-1);
}
int qry(int u,int u2,int w,int d){
	if(!u) return 0;
	int v = w >> d & 1;
	if(sz[tr[u][!v]] - sz[tr[u2][!v]]) return qry(tr[u][!v],tr[u2][!v],w,d-1) + (1<<d);
	return qry(tr[u][v],tr[u2][v],w,d-1);
}

int c[maxn],wa[maxn],wb[maxn],sa[maxn],rk[maxn],h[maxn];
void BuildSA(char *s,int n,int m){
	int *x=wa,*y=wb;
	rep(i,0,m-1) c[i] = 0;
	rep(i,0,n-1) c[x[i] = s[i]]++;
	rep(i,1,m-1) c[i] += c[i-1];
	per(i,n-1,0) sa[--c[x[i]]] = i;
	for(int j=1,p=0;j<n && p < n;j<<=1,m=p){
		p=0;
		rep(i,n-j,n-1) y[p++] = i;
		rep(i,0,n-1) if(sa[i] >= j) y[p++]=sa[i]-j;
		rep(i,0,m-1) c[i] = 0;
		rep(i,0,n-1) c[x[i]]++;
		rep(i,1,m-1) c[i] += c[i-1];
		per(i,n-1,0) sa[--c[x[y[i]]]] = y[i];
		p=0;
		swap(x,y);
		x[sa[0]] = p++;
		rep(i,1,n-1) 
			x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i]+j] == y[sa[i-1]+j]) ? p-1 : p++;
 	}
 	rep(i,0,n-1) rk[sa[i]] = i;
 	for(int i=0,k=0;i<n-1;h[rk[i++]] = k)
 		for(k?k--:0;s[i+k] == s[sa[rk[i]-1]+k];k++);
}

int lc[maxn],rc[maxn],q[maxn],tp,w[maxn];
int ans = 0;

void Solve(int u,int l,int r){
	if(!u) return;
	if(u-l < r-u+1){
		rep(i,l,u-1) ans = max(ans , h[u] + qry(rt[r],rt[u-1],w[sa[i]],lim-1)); 
	}
	else{
		rep(i,u,r) ans = max(ans , h[u] + qry(rt[u-1],rt[l-1],w[sa[i]],lim-1)); 
	}
	Solve(lc[u],l,u-1),Solve(rc[u],u,r);
}

int main(){
	scanf("%d",&n);
	scanf("%s",s);
	BuildSA(s,n+1,250);
	rep(i,0,n-1) scanf("%d",&w[i]);
	rep(i,1,n) ins(rt[i]=rt[i-1],w[sa[i]],lim-1);
	rep(i,2,n){
		for(;tp && h[q[tp]] >= h[i];tp--)
			rc[q[tp]] = lc[i],lc[i] = q[tp];
		q[++tp] = i;
	}
	for(;tp>1;tp--)
		rc[q[tp-1]] = q[tp];
	Solve(q[1],1,n); 
	printf("%d\n",ans);
}

NOI 2018 你的名字

每次詢問TT有多少個子串aa滿足aa不是S[l...r]S[l...r]的子串。

後綴自動機fail\texttt{fail}樹上線段樹合併得到每個節點的endpos(right集合)\texttt{endpos}(\texttt{right集合})
TT扔到SS上匹配,
加入TiT_i,判斷是否失配:
判斷這個字符的下一個點的最短字符串是否在S[l...r]S[l...r]出現過,即下一個點的rightright集合中是否有[l+minl1,r][l+min_l-1,r]中的endposendpos

失配了,跳failfail後,或者是沒失配跳到下一個點,可能需要縮小當前長度,求[l+minl1,r][l+min_l-1,r]中最大的endposendpos再減去ll,再和原長度取min\min即可得到對於TT的每個前綴TiT_i,最靠前的ll使得Tl,iT_{l,i}SS中出現過。
然後把這些信息扔到TT的後綴自動機上DPDP求出他們本質不同的部分即可。

AC Code\mathcal AC\ Code

//ION0202 fister.
#include<bits/stdc++.h>
#define maxn 2000005
#define maxp maxn * 11
#define maxc 26
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char mstar;

char S[maxn],T[maxn];
int n,m;

int rt[maxn],lc[maxp],rc[maxp],mx[maxp],tot;
void ins(int &u,int l,int r,int p){
	if(!u) u = ++tot;
	mx[u] = max(mx[u] , p);
	if(l == r) return;
	int m = l+r>>1;
	p <= m ? ins(lc[u],l,m,p) : ins(rc[u],m+1,r,p);
}
int qry(int u,int l,int r,int ql,int qr){
	if(ql>r||l>qr||ql>qr||!u) return 0;
	if(ql<=l&&r<=qr) return mx[u];
	int m = l+r>>1;
	int t = qry(rc[u],m+1,r,ql,qr);
	if(t) return t;
	return qry(lc[u],l,m,ql,qr);
}
void merge(int &u,int l,int r){
	if(!l || !r) return (void)(u = l+r);
	u = ++tot;
	mx[u] = max(mx[l] , mx[r]);
	merge(lc[u],lc[l],lc[r]) , merge(rc[u],rc[l],rc[r]);
}
bool cmp(const int &u,const int &v);
bool cmp2(const int &u,const int &v);
struct SAM{
	int tr[maxn][maxc],fa[maxn],len[maxn],tot,last,pos[maxn],Len,c[maxn];
	int ans[maxn],tg[maxn];
	void ins(int c){
		int cur = ++tot , p = last , q;
		len[last = cur] = len[p] + 1;
		for(;~p && !tr[p][c];p=fa[p]) tr[p][c] = cur;
		if(p == -1) fa[cur] = 0;
		else if(len[q = tr[p][c]] == len[p] + 1) fa[cur] = q;
		else{
			int cl=++tot;
			memcpy(tr[cl],tr[q],sizeof tr[q]);
			fa[cl] = fa[q] , len[cl] = len[p] + 1;
			for(;~p && tr[p][c] == q;tr[p][c] = cl,p=fa[p]);
			fa[q] = fa[cur] = cl;
		}
	}
	void init(char *S,int L){
		last = tot = 0;
		memset(tr,0,sizeof(tr[0])*(L+1)*2);
		memset(fa,0,sizeof(int)*(L+1)*2);
		memset(len,0,sizeof(int)*(L+1)*2);
		fa[0] = -1;
		rep(i,0,L-1) ins(S[i]-'a'),pos[i+1] = last;
		rep(i,1,tot) ans[i] = len[fa[i]] , tg[i] = 0;
		tg[0] = 0;
		Len = L;
	}
	void Build(){
		rep(i,1,Len) ::ins(rt[pos[i]],1,Len,i);
		rep(i,0,tot) c[i] = i;
		sort(c,c+tot+1,cmp);
		per(i,tot,0){
			int u = c[i];
			if(~fa[u])
				merge(rt[fa[u]],rt[fa[u]],rt[u]);
		}
	}
	LL query(char *S,int L,int l,int r,SAM &B){
		int u = 0,nl = 0 , bu = 0;
		rep(i,0,L-1){
			int v = S[i] - 'a' , p = tr[u][v];
			int t = p ? qry(rt[p],1,Len,l+len[fa[p]],r) : 0;
			for(;~u && !t;){
				u = fa[u] , p = tr[u][v];
				t = p ? qry(rt[p],1,Len,l+len[fa[p]],r) : 0;
			}
			if(u == -1) nl = 0 , u = 0;
			else nl = min(nl + 1 , min(qry(rt[u],1,Len,l,r)-l+1,len[u]) + 1) , u = tr[u][v];
			nl = min(nl , qry(rt[u],1,Len,l,r)-l+1);
			bu = B.tr[bu][v];
			B.tg[bu] = nl;
		}
		
		LL ret = 0;
		rep(i,0,B.tot) B.c[i] = i;
		sort(B.c,B.c+B.tot+1,cmp2);
		per(i,B.tot,1){
			int u = B.c[i];
			B.tg[B.fa[u]] = max(B.tg[B.fa[u]] , B.tg[u]);
			ret += min(max(B.ans[u] , B.tg[u]) , B.len[u]) - B.len[B.fa[u]];
		}
		return ret;
	}
}SA,TA;
bool cmp(const int &u,const int &v){ return SA.len[u] < SA.len[v]; }
bool cmp2(const int &u,const int &v){ return TA.len[u] < TA.len[v]; }


char mend;

int main(){
	
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	
	scanf("%s",S);
	n = strlen(S);
	SA.init(S,n);
	SA.Build();
	int q;scanf("%d",&q);
	for(;q--;){
		scanf("%s",T);
		m = strlen(T);
		TA.init(T,m);
		LL ans=0;
		rep(i,1,TA.tot)
			ans += TA.len[i] - TA.len[TA.fa[i]];
		int l,r;scanf("%d%d",&l,&r);
		ans -= SA.query(T,m,l,r,TA);
		printf("%lld\n",ans);
	}
}

「PA 2019」Podatki drogowe

給定一棵邊權爲npin^{p_i}的樹,在(n2)\binom n2個點對中的最短路徑中求第kk短路徑。
n25000n \leq 25000

套路題。
二分答案。
發現答案值域很大但是可取的值很少(只有O(n2)O(n^2)種)。
那麼每次隨機一個(在二分範圍內)可取的值(方案),然後判斷比他短的有沒有kk個即可完成二分答案。
期望次數lnn\ln n次。

如何比較兩條路徑大小?
發現路徑的長度之和永遠不會進位。
所以路徑的長度即爲對位相加。
比較大小就相當於比較字符串大小,用哈希求出LCPLCP後即可判斷大小。

如何快速求出比一條路徑短的路徑數?
點分治,對於每個分治中心xx,維護xx到其點分樹子樹中每個點uu的字符串。
因爲點分樹子樹是樹上聯通塊,具有樹形結構,我們可以用可持久化線段樹維護xx到每個點uu的字符串以及字符串的任意一個區間的哈希值。

發現可以合併兩個字符串,在可持久化線段樹上表現爲對位哈希值相加。
於是將一個點分中心周圍的所有字符串排個序,每次詢問的時候twopointerstwo-pointers即可求出經過點分中心的字符串有多少小於詢問串。

一次字符串比較是O(logn)O(\log n)的,於是預處理是O(nlog3n)O(n \log^3 n)的。
一次詢問是O(nlogn)O(n\log n)的。
加上二分是O(nlog2n)O(n\log ^2n)的。

隨機一個在二分範圍內的答案,因爲我們是用twopointerstwo-pointers處理詢問的,所以我們自然可以得到每個點分中心,選一個點爲一端時另一端的在什麼範圍內纔在二分範圍內。

本來我是沿着租酥雨的思路想寫點分的,但是中途看了一下EI的博客然後覺得點分還要去重實在是太噁心了,就寫了邊分治

注意因爲是隨機二分,所以你不能像普通二分一樣+1+1,在這個題甚至連L,RL,R是什麼路徑都不需要維護,只需要維護可選值域即可。
所以就迭代100100次即可通過此題。

AC Code\mathcal AC \ Code

//next time.
#include<bits/stdc++.h>
#define maxn 50005
#define lim 16
#define LL long long
#define maxp maxn * lim * lim
#define mod 1000000007
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

LL K;
int n,cnt_p;
int info[maxn],Prev[maxn<<1],to[maxn<<1],cst[maxn<<1],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
int fir[maxn],nxt[maxn<<1],tar[maxn<<1],cw[maxn<<1],cnte=1;
void add(int u,int v,int c){ nxt[++cnte]=fir[u],fir[u]=cnte,tar[cnte]=v,cw[cnte]=c; }

int sz[maxn],vis[maxn];
void dfs0(int u,int ff){
	int p = 0;
	for(int i=info[u],v;i;i=Prev[i]) if((v=to[i])^ff){
		dfs0(v,u);
		if(!p){
			add(u,v,cst[i]),add(v,u,cst[i]);
			p = u;
		}
		else{
			int t = ++cnt_p;
			add(p,t,0),add(t,p,0);
			add(t,v,cst[i]),add(v,t,cst[i]);
			p = t;
		}
	}
}

void dfs1(int u,int ff,int tsz,int &mn,int &rt){
	sz[u] = 1;
	for(int i=fir[u],v;i;i=nxt[i]) if((v=tar[i])^ff && !vis[i/2]){
		dfs1(v,u,tsz,mn,rt);
		int t = max(sz[v] , tsz - sz[v]);
		if(t < mn) mn = t , rt = i/2;
		sz[u] += sz[v];
	}
}

int Gert(int u,int tsz){
	int mn = 0x3f3f3f3f , rt = -1;
	dfs1(u,0,tsz,mn,rt);
	return rt;
}

int rt[maxn],lc[maxp],rc[maxp],sm[maxp],tot;
vector<int>G[maxn],rG[maxn],mx[maxn],mn[maxn],pt[maxn];
LL hs[maxp],sj[maxn];
void ins(int &u,int l,int r,int p){
	if(!p) return;
	++tot;
	hs[tot] = hs[u] + sj[p];
	sm[tot] = sm[u] + 1;
	lc[tot] = lc[u];
	rc[tot] = rc[u];
	u = tot;
	if(l==r) return;
	int m = l+r>>1;
	p <= m ? ins(lc[u],l,m,p) : ins(rc[u],m+1,r,p);
}
int comp(int u,int v,int l,int r,int u2=0,int v2=0){// 0 equal -1 greater 1 less
	if(hs[u] + hs[u2] == hs[v] + hs[v2]) return 0;
	if(l == r){
		int a = sm[u] + sm[u2] , b = sm[v] + sm[v2];
		return a < b ? 1 : a > b ? -1 : 0;
	}
	int m = l+r>>1;
	int t = comp(rc[u],rc[v],m+1,r,rc[u2],rc[v2]);
	if(t) return t;
	return comp(lc[u],lc[v],l,m,lc[u2],lc[v2]);
}
bool cmp(const int &u,const int &v){
	return comp(u,v,1,n) == 1;
}
struct node{
	int u,v;
	node(const int &u=0,const int &v=0):u(u),v(v){}
	bool operator <(const node &B)const{
		int t = comp(u,B.u,1,n,v,B.v);
		return t == 1;
	}
};

void dfs2(int u,int ff,vector<int>&G){
	sz[u] = 1;
	if(u <= n)
		G.push_back(rt[u]);
	for(int i=fir[u],v;i;i=nxt[i])
		if((v=tar[i])^ff && !vis[i / 2]){
			rt[v] = rt[u];
			ins(rt[v],1,n,cw[i]);
			dfs2(v,u,G);
			sz[u] += sz[v];
		}
}


void Solve(int u){
	if(u == -1) return;
	vis[u] = 1;
	rt[tar[u<<1]] = 0;
	ins(rt[tar[u<<1]],1,n,cw[u<<1]);
	rt[tar[u<<1|1]] = 0;
	dfs2(tar[u<<1],0,G[u]);
	dfs2(tar[u<<1|1],0,rG[u]);
	mn[u].resize(G[u].size());
	mx[u].resize(G[u].size());
	pt[u].resize(G[u].size());
	rep(i,0,G[u].size()-1) 
		mx[u][i] = rG[u].size(),
		mn[u][i] = 0;
	sort(rG[u].begin(),rG[u].end(),cmp);
	sort(G[u].begin(),G[u].end(),cmp);
	Solve(Gert(tar[u<<1],sz[tar[u<<1]]));
	Solve(Gert(tar[u<<1|1],sz[tar[u<<1|1]]));
}
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
int calc(int u,int v,int l,int r){
	if(l == r) return 1ll * (sm[u] + sm[v]) * Pow(n , l) % mod;
	int m = l+r>>1;
	return (calc(lc[u],lc[v],l,m) + calc(rc[u],rc[v],m+1,r)) % mod;
}

int main(){
	scanf("%d%lld",&n,&K);
	rep(i,1,n) sj[i] = rand() * 1ll << 45 | rand() * 1ll << 30 | rand() * 1ll << 15 | rand();
	rep(i,1,n-1){
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		Node(u,v,c),Node(v,u,c);
	}
	cnt_p = n;
	dfs0(1,0);
	Solve(Gert(1,cnt_p));
	LL al = 1ll * n * (n-1) / 2;
	
	node mid;
	for(int T=1;T<=100;T++){
		LL t = (rand() * 1ll << 30 | rand() * 1ll << 15 | rand()) % al + 1;
		rep(i,1,cnte/2){
			rep(j,0,G[i].size()-1){
				if(mx[i][j] - mn[i][j] >= t){ mid = node(G[i][j],rG[i][mn[i][j]+t-1]) , t = -1; break;}
				else t -= mx[i][j] - mn[i][j];
			}
			if(t < 0) break;
		}
		LL hd = 0;
		rep(i,1,cnte/2) if(G[i].size()){
			pt[i][0] = mx[i][0];
			rep(j,0,G[i].size()-1){
				if(j) pt[i][j] = min(mx[i][j],pt[i][j-1]);
				for(;pt[i][j] > mn[i][j] && mid < node(G[i][j],rG[i][pt[i][j]-1]);pt[i][j]--);
				hd += pt[i][j];
			}
		}
		if(hd == K)
			break;
		al = 0;
		if(hd < K){
			rep(i,1,cnte/2) rep(j,0,G[i].size()-1) mn[i][j] = pt[i][j] , al += mx[i][j] - mn[i][j];
		}
		else{
			rep(i,1,cnte/2) rep(j,0,G[i].size()-1) mx[i][j] = pt[i][j] , al += mx[i][j] - mn[i][j];;
		}
	}
	printf("%d\n",(calc(mid.u,mid.v,1,n)+mod)%mod);
}

剩下77個題下午明天寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章