【TEST190411】 树剖 + KMP&EXCRT

树剖 + KMP,EXCRT
摘要由CSDN通过智能技术生成

set

发现序列后一位必然是 A A A x x x后第一个 > A x >A_x >Ax的位置 y y y,类似于树上 x → f a x x\to fa_x xfax的关系。
所以单调栈建数,树剖维护单调修改,链查询,特判一下即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+12;

int n,m,a[N],w[N],rt;
int f[N],son[N],sz[N],top[N],dfn;
int dep[N],df[N],rv[N];
int head[N],to[N],nxt[N],tot;

inline char gc()
{
    static char buf[(1<<15)];static int p1=0,p2=0;
    if(p1==p2) p1=0,p2=fread(buf,1,(1<<15),stdin);
    return (p1==p2)?EOF:buf[p1++];
}

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

template<class T>inline void prit(T x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) prit(x/10);putchar('0'+(x%10));
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

void dfs1(int x)
{
	int i,j;sz[x]=1;son[x]=0;
	dep[x]=dep[f[x]]+1;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];dfs1(j);
		sz[x]+=sz[j];
		if((!son[x]) || sz[j]>sz[son[x]]) son[x]=j;
	}
}

void dfs2(int x,int tpo)
{
	df[x]=++dfn;rv[dfn]=x;top[x]=tpo;
	if(!son[x]) return;
	dfs2(son[x],tpo);int i,j;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==son[x]) continue;
		dfs2(j,j);
	}
}

inline int LCA(int x,int y)
{
	for(;top[x]!=top[y];x=f[top[x]])
	 if(dep[top[x]]<dep[top[y]]) swap(x,y);
	if(dep[x]<dep[y]) swap(x,y);
	return y;
}

namespace SGT{
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
#define lcc lc,l,mid
#define rcc rc,mid+1,r
ll slf[N<<2],af[N<<2],z,chg[N];//self as_fat

void build(int k,int l,int r)
{
	if(l==r){slf[k]=(ll)w[rv[l]];return;}
	build(lcc);build(rcc);
	slf[k]=slf[lc]+slf[rc];
}

void ad(int k,int l,int r,int pos)
{
	slf[k]+=z;af[k]+=z;
	if(l==r) return;
	if(pos<=mid) ad(lcc,pos);
	else ad(rcc,pos);
}

ll query(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return slf[k]+af[k];
	if(R<=mid) return query(lcc,L,R);
	if(L>mid) return query(rcc,L,R);
	return query(lcc,L,R)+query(rcc,L,R);
}

inline ll ask(int x,int y)
{
	ll re=0LL;
	for(;top[x]!=top[y];x=f[top[x]]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		re+=query(1,1,n,df[top[x]],df[x]);
	}
	if(dep[x]<dep[y]) swap(x,y);
	re+=query(1,1,n,df[y],df[x]);
	return re;
}

void sol()
{
	int op,x,y,sme;
	rd(op);rd(x);rd(y);
	if(op==1){z=y;ad(1,1,n,df[x]);chg[x]+=z;return;}
	if(dep[x]<dep[y]) swap(x,y);sme=LCA(x,y);
	if(sme==rt || (sme==y && f[y]==rt)){puts("?");return;}
	ll re=ask(x,y);
	if(sme!=x && sme!=y){
        re-=(chg[x]+chg[y]);re+=chg[sme];
        if(f[sme]!=rt) re+=chg[f[sme]];
	}else{
         re-=chg[x];sme=f[sme];re+=((chg[sme]<<1)+w[sme]);
         if(f[sme]!=rt) re+=chg[f[sme]];
	}
	prit(re);putchar('\n');
}

}

int stk[N],stp;
int main(){
	freopen("set.in","r",stdin);
	freopen("set.out","w",stdout);
	int i;rd(n);rd(m);
	for(i=1;i<=n;++i){
		rd(a[i]);
		for(;stp>0 && a[stk[stp]]<a[i];--stp){
			lk(i,stk[stp]);f[stk[stp]]=i;
		}
		stk[++stp]=i;
	}
	for(i=1;i<=n;++i) rd(w[i]);
	n++;rt=n;
	for(;stp>0;--stp) {lk(rt,stk[stp]);f[stk[stp]]=rt;}
	dfs1(rt);dfs2(rt,rt);
	SGT::build(1,1,n);
	for(;m;--m) SGT::sol();
	fclose(stdin);fclose(stdout);
	return 0;
}

blue

循环右移的循环节 x ≤ n x\leq n xn

双射函数的置换循环节 l c m ( l e n i ) , ∑ l e n i = 26 lcm(len_i),\sum len_i=26 lcm(leni),leni=26(打表发现 ≤ 1260 \leq 1260 1260)

所以整体循环节 ≤ 1260 n \leq 1260n 1260n,模数是int级别的

固定左移位数 x x x,操作次数 k k k

a ⋅ k ≡ x ( m o d n ) , x ≡ Δ ( p o s t i − p o s s i + x ) ( m o d l e n b e l t i ) a·k\equiv x\pmod{n},x\equiv \Delta(pos_{t_i}-pos_{s_{i+x}})\pmod{len_{bel_{t_i}}} akx(modn),xΔ(postipossi+x)(modlenbelti)

EXCRT求解 26 + 1 26+1 26+1个线性同余方程的解即可。
(其中 p o s t i pos_{t_i} posti表示字符 t i t_i ti在环中的位置, b e l t i bel_{t_i} belti表示字符 t i t_i ti所属环)

可以匹配上显然有两个前提条件:

  • b e l t i = b e l s t i + x bel_{t_i}=bel_{s_{t_i+x}} belti=belsti+x
  • 对于每一个环,环上所有位置差相同

暴力求解是 O ( n 2 ) O(n^2) O(n2)的(因为每个位置都会列一个方程)

发现本质上只有 26 + 1 26+1 26+1个方程,考虑优化(下面的我就没想到了QWQ:

  • t i → b e l t i , s i → b e l s i t_i\to bel_{t_i},s_{i}\to bel_{s_i} tibelti,sibelsi,KMP求出所有匹配上的位置,满足条件1
  • 对于每个环,每个位置与前驱最近的属于同一个环的字符所处环位置做差分,KMP求匹配,满足条件2

只需要对于所有可行位置求解,总时间复杂度 O ( 27 n log ⁡ n ) O(27n\log n) O(27nlogn)

我的 n 2 n^2 n2暴力:

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+20;
const int inf=0x7f7f7f7f;
//max lcm(1...26) 1260
//max (1260,n) 251998740

int n,A,B,f[27],ans=inf,g;
char s[N<<1],t[N<<1];
ll X,Y;

char cp;
template<class T>inline void rd(T &x)
{
	cp=getchar();x=0;int f=0;
	for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline void dn(int &x,int y){if(y<x) x=y;}
inline int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
inline void exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b) {x=1;y=0;return;}
	exgcd(b,a%b,y,x);y-=(a/b)*x;
}

namespace CRT{
struct pr{int val,p;}e[N],ex[N];
int dr[27],nw,md,num;	

inline int mk(int n)
{
	int i,res,rp,g,j;num=0;
	memset(dr,0xff,sizeof(dr));
	for(i=1;i<=n;++i) if(e[i].p<=26){
		if(dr[e[i].p]==-1) dr[e[i].p]=e[i].val,ex[++num]=e[i];
		else if(e[i].val!=dr[e[i].p]) return inf;
	}else ex[++num]=e[i];
	nw=0;md=0;
  	for(i=1;i<=num;++i){
		rp=ex[i].p;
		if((!nw)&&(!md)) {nw=ex[i].val;md=rp;continue;}
		res=(ex[i].val%rp-nw%rp);if(res<0) res+=rp;
		g=gcd(md,rp);if(res%g) return inf;res/=g;
		exgcd(md%rp,rp,X,Y);X%=rp;if(X<0) X+=rp;
		j=md;md=md/g*rp;res=(ll)res*j%md;
		nw=(nw+(ll)X*res%md)%md;
	}
	if(nw==0) return md;
	return nw;
}
}

int bel[27],rk[27],cir,len[27];
vector<int>hv[27];
inline void pre()
{
	int i,j;
	for(i=0;i<26;++i) if(!bel[i]){
		bel[i]=++cir;len[cir]=1;
		rk[i]=0;hv[cir].pb(i);
		for(j=f[i];j!=i;j=f[j]){
			bel[j]=cir;hv[cir].pb(j);
			rk[j]=len[cir]++;
		}
	}
	for(i=0;i<26;++i){
		j=bel[i];bel[i]=0;
		f[i]=hv[j][(rk[i]+B)%len[j]];
	}
	cir=0;
	for(i=0;i<26;++i) if(!bel[i]){
		bel[i]=++cir;len[cir]=1;
		rk[i]=0;
		for(j=f[i];j!=i;j=f[j]){
			bel[j]=cir;rk[j]=len[cir]++;
		}
	}
}
inline int nt(int x){return x<=n?x:x-n;}
inline int cal(int dlt)
{
	int i,j,x,y;
	for(i=1;i<=n;++i){
		x=t[i];y=s[nt(i+dlt)];j=bel[x];
		if(j!=bel[y]) return inf;j=len[j];
		CRT::e[i].p=j;
		CRT::e[i].val=(rk[x]-rk[y]+j)%j;
	}
	if(A){
		int md=n/g;dlt/=g;
		exgcd(A/g,md,X,Y);
		X=X%md;if(X<0) X+=md;
		CRT::e[n+1].p=md;
		CRT::e[n+1].val=(ll)dlt*X%md;
		return CRT::mk(n+1);
	}
	return CRT::mk(n);
}

int main(){
	freopen("blue.in","r",stdin);
	freopen("blue.out","w",stdout);
	int i,j;
	rd(n);rd(A);rd(B);A%=n;
	scanf("%s",s);
	for(i=0;i<26;++i) f[i]=s[i]-'a';
	scanf("%s%s",s+1,t+1);
	if(A==0 && B==0){
		for(i=1;i<=n;++i) if(s[i]!=t[i]) break;
	    printf((i>n)?"1":"-1");return 0;
	}
	pre();if(A) g=gcd(A,n);
	for(i=1;i<=n;++i) s[i]-='a',t[i]-='a';
	if(A==0) ans=cal(0);
	else if((ll)(n/g)*n<=9000001LL) for(i=0;i<n;i+=g) dn(ans,cal(i));
	if(ans<inf) printf("%d",ans);
	else printf("-1");
	fclose(stdin);fclose(stdout);
	return 0;
}

std

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long 
int ans=2147483647,z,y,x,M;//ans will not be greater than n*1260,otherwise ans is -1
int nxt[200050],la,lb,f[35],lis[200050],lcnt,np[400050],bel[35],a,b;
int id[35],clen[35],nb[35],tmp,cnt,l1[200050],l2[400050],lst[35],ep[35];
char s[400050],t[200050],s1[35];
bool vis[400050],pvis[400050];
int gcd(int a,int b){return !(a%b)?b:gcd(b,a%b);}
void dfs(int u,int lb,int nu){
	bel[u]=lb;
	id[u]=nu;
	tmp++;
	if (!bel[f[u]])dfs(f[u],lb,nu+1);
}
void kmp(){
	nxt[0]=-1;int j=-1;
	for (int i=1;i<lb;i++){
		while (j!=-1&&l1[j+1]!=l1[i])j=nxt[j];
		if (l1[j+1]==l1[i])j++;
		nxt[i]=j;
	}
}
void getp(){
	int j=-1;
	for (int i=0;i<la;i++){
		while (j!=-1&&l2[i]!=l1[j+1])j=nxt[j];
		if (l1[j+1]==l2[i])j++;
		if (j==lb-1)vis[i]=1,j=nxt[j];
	}
} 
void inv(ll a,ll b) {
    if (a%b==0){z=0;y=1;return;}
    inv(b,a%b);
    ll r=z;
    z=y,y=r-a/b*y;
}
bool solve(ll A,ll k,int m){
	int d;
	d=gcd(A,m),m/=d,A/=d;
	if ((k%d))return 0;
	k/=d;
	inv(A,m);
	k*=z;A=k-x;//k*z may greater than 2147483647
	d=gcd(M,m);
	if ((A%d))return 0;
	inv(M/d,m/d);
	int kp=A/d*1ll*z%(m/d);
	x+=kp*M;
	M*=m/d;
	return 1;
}
int main(){
        freopen("blue.in","r",stdin);
        freopen("blue.out","w",stdout);
	scanf("%d%d%d",&lb,&a,&b);
	a%=lb;
	la=lb*2;
	scanf("%s",s1);
	for (int i=0;i<26;i++)f[i]=s1[i]-'a';
	for (int i=0;i<26;i++)if (!bel[i]){dfs(i,++cnt,1);clen[cnt]=tmp;nb[cnt]=b%tmp;tmp=0;}
	scanf("%s%s",s,t);
	for (int i=0;i<lb;i++)s[i+lb]=s[i];
	for (int i=0;i<lb;i++)l1[i]=bel[t[i]-'a'];
	for (int i=0;i<la;i++)l2[i]=bel[s[i]-'a'];
	kmp();getp();//match with ring's num
	memcpy(pvis,vis,sizeof pvis);
	memset(vis,0,sizeof vis);
	for (int i=0;i<lb;i++){
		if (lst[l2[i]])l2[i]=(id[s[i]-'a']-lst[l2[i]]+clen[l2[i]])%clen[l2[i]];
		else np[i]=1;
		lst[bel[s[i]-'a']]=id[s[i]-'a'];
	}
	for (int i=0;i<lb;i++)if (np[i])l2[i]=(id[s[i]-'a']-lst[bel[s[i]-'a']]+clen[bel[s[i]-'a']])%clen[bel[s[i]-'a']];
	for (int i=0;i<lb;i++)l2[i+lb]=l2[i];
	memset(lst,0,sizeof lst);
	memset(nxt,0,sizeof nxt);
	memset(np,0,sizeof np);
	memset(ep,-1,sizeof ep);
	for (int i=0;i<lb;i++){
		if (lst[l1[i]])l1[i]=(id[t[i]-'a']-lst[l1[i]]+clen[l1[i]])%clen[l1[i]];
		else np[i]=1;
		lst[bel[t[i]-'a']]=id[t[i]-'a'];
		ep[bel[t[i]-'a']]=i;
	}
	for (int i=0;i<lb;i++)if (np[i])
	l1[i]=(id[t[i]-'a']-lst[bel[t[i]-'a']]+clen[bel[t[i]-'a']])%clen[bel[t[i]-'a']];
	kmp();getp();//match with ring's differences 
	int k;
	for (int i=lb-1;i<la-1;i++)if (vis[i]&&pvis[i])lis[++lcnt]=(i-lb+1)%lb;
	for (int i=1;i<=lcnt;i++){
		x=0,M=1; 
		bool flag=1;//solve congruence equations
		for (int j=1;j<=cnt&&flag;j++)if(ep[j]!=-1){
			int pos=(lis[i]+ep[j])%lb;
			k=(id[t[ep[j]]-'a']-id[s[pos]-'a']+clen[j])%clen[j];
			if (!nb[j]){if (!k)continue;flag=0;continue;}
			flag&=solve(nb[j],k,clen[j]);
		}
		if (!a){if (lis[i])continue;}
		else flag&=solve(a,lis[i],lb);
		if (!flag)continue;
		x<=0?x+=M:0;
		ans=std::min(ans,x);
	}
	if (ans>1e9)printf("-1\n");
	else printf("%d\n",ans);
}

总结

以后T1这种签到题一定要快准狠的切了

T2其实就差一步KMP了,为什么我没有想到相对位置可以差分呢QWQ,难受

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值