2020.7.28集训

string

description

小 A 最近喜欢上了一个字母游戏,这个游戏的大意是这样的:有 n n n 个无限长的循环字符串,所谓循环字符串,就是由某一个子串重复叠加而成。现在想知道最早在哪一位,这 n n n 个字符串位于该位的字母相同。小 A 对于简单难度的关卡简直手到擒来,但是对于困难难度的关卡他就一筹莫展了。这时他想到了你,一个机智的程序员,他希望你能够帮他编写程序求出答案。我们保证该子串中的字母互不重复,但区分大小写。

n ≤ 3 × 1 0 5 n\leq 3\times 10^5 n3×105

solution

最后一句话是重点

我们保证该子串中的字母互不重复,但区分大小写。

相当于是转化成了 n n n个同余方程,因为模数不互质,直接上excrt

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=30005;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

# define int __int128

int n;
char s[N][60];
int a[N],p[N],x,y;
int ans=-1;

int exgcd(int a,int b,int &x,int &y){
	if(!b){
		x=1,y=0;
		return a;
	}
	int d=exgcd(b,a%b,x,y);
	int z=y;
	y=x-a/b*y;
	x=z;
	return d;
}

int mul(int a,int b,int p){
	int res=0;
	while(b){
		if(b&1)res+=a,res%=p;
		a+=a,a%=p;
		b>>=1;
	}
	return res;
}

int excrt(){
	int ans=a[1],M=p[1];
	Rep(i,2,n){
		int GCD=exgcd(M,p[i],x,y);
		int val=((a[i]-ans)%p[i]+p[i])%p[i];
		if(val%GCD!=0)return -1;
		x=(x%p[i]+p[i])%p[i];
		int t=mul(x,val/GCD,p[i]);
		ans+=t*M;
		M=M/GCD*p[i];
		ans=(ans%M+M)%M;
	}
	return ans;
}

signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	read(n);
	Rep(i,1,n)scanf("%s",s[i]);
	Rep(i,1,n)p[i]=strlen(s[i]);
	Rep(i,'a','z'){
		bool flag=true;
		Rep(j,1,n){
			int pos=-1;
			for(int k=0;k<p[j];k++)if(s[j][k]==i){pos=k;break;}	
			if(pos==-1){flag=false;break;}
			a[j]=pos;
		}
		if(!flag)continue;
		int val=excrt();
		if(val==-1)continue;
		if(ans==-1)ans=val;
		else ans=min(ans,val);
	}
	Rep(i,'A','Z'){
		bool flag=true;
		Rep(j,1,n){
			int pos=-1;
			for(int k=0;k<p[j];k++)if(s[j][k]==i){pos=k;break;}	
			if(pos==-1){flag=false;break;}
			a[j]=pos;
		}
		if(!flag)continue;
		int val=excrt();
		if(val==-1)continue;
		if(ans==-1)ans=val;
		else ans=min(ans,val);
	}
	if(ans==-1)puts("-1");
	else printf("%llu\n",(unsigned long long)ans+1);
	return 0;
}
apple

description

维护一棵树

1 x 询问 询问:以某个节点 X 为根的子树的权值和
2 x k 修改 修改:将以某点 X 为根的子树上所有节点的权加上一个数 k
3 x y 修改 修改:将以某点 X 为根的整棵子树移到另一个点 Y,并成为 Y 的子树

solution
操作3灰常的恶心,先看前两个,那就是非常简单的一个dfs序
操作3本质上是交换了了dfs序,遇到交换问题,考虑平衡树

但是交换之后子树大小也会相应的变化

怎么办呢?我们可以把每个节点拆成两个,分别表示他子树在dfs区间上的左端点右端点
然后正常的做就可以了,因为每个点被算了两遍,所以操作1的答案要/2

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=3e5+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

# define int long long

int n,m;
int head[N],cnt;
int a[N];
int dfn[N];

struct Edge{
	int to,next;	
}e[N<<1];

void add(int x,int y){
	e[++cnt]=(Edge){y,head[x]},head[x]=cnt;	
}

struct FHQ{
	int fa[N],son[N][2],siz[N],tag[N],treap[N],sum[N],val[N];
	int rt,tot;
	void insert(int u,int k){
		treap[u]=rand();
		siz[u]=1;
		sum[u]=val[u]=k;
		rt=merge(rt,u);
	}
	void pushup(int x){
		if(son[x][0])fa[son[x][0]]=x;
		if(son[x][1])fa[son[x][1]]=x;
		siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
		sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x];
	}
	void pushdown(int x){
		if(!tag[x])return;
		sum[son[x][0]]+=tag[x]*siz[son[x][0]],val[son[x][0]]+=tag[x];
		tag[son[x][0]]+=tag[x];
		sum[son[x][1]]+=tag[x]*siz[son[x][1]],val[son[x][1]]+=tag[x];
		tag[son[x][1]]+=tag[x];
		tag[x]=0;	
	}
	int merge(int u,int v){
		if(!u||!v)return u|v;
		int rt;
		if(treap[u]<treap[v]){
			pushdown(rt=u);
			son[u][1]=merge(son[u][1],v);
		}
		else{
			pushdown(rt=v);	
			son[v][0]=merge(u,son[v][0]);
		}
		return pushup(rt),rt;
	}
	void split(int o,int &u,int &v,int k){
		if(!o){u=v=0;return;}
		int rank=siz[son[o][0]]+1;
		fa[o]=0;
		pushdown(o);
		if(rank<=k)split(son[u=o][1],son[o][1],v,k-rank);
		else split(son[v=o][0],u,son[o][0],k);
		pushup(o);
	}
	int rnk(int x){
		int res=siz[son[x][0]]+1;
		while(fa[x]){
			if(son[fa[x]][1]==x)res+=siz[son[fa[x]][0]]+1;
			x=fa[x];	
		}
		return res;
	}
}treap;

void dfs(int u,int fa){
	treap.insert(u,a[u]);
	RepG(i,u){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
	}	
	treap.insert(n+u,a[u]);
}

int query(int x){
	int l=treap.rnk(x)-1,r=treap.rnk(n+x);
	int lft,mid,rht;
	treap.split(treap.rt,lft,rht,r);
	treap.split(lft,lft,mid,l);
	int res=treap.sum[mid];
	treap.rt=treap.merge(treap.merge(lft,mid),rht);
	return res;	
}

void update(int x,int k){
	int l=treap.rnk(x)-1,r=treap.rnk(n+x);
	int lft,mid,rht;
	treap.split(treap.rt,lft,rht,r);
	treap.split(lft,lft,mid,l);
	treap.val[mid]+=k;
	treap.sum[mid]+=treap.siz[mid]*k;
	treap.tag[mid]+=k;
	treap.rt=treap.merge(treap.merge(lft,mid),rht);
}

void linkcut(int x,int y){
	int l=treap.rnk(x)-1,r=treap.rnk(n+x);
	int lft,mid,rht;
	treap.split(treap.rt,lft,rht,r);
	treap.split(lft,lft,mid,l);
	treap.rt=treap.merge(lft,rht);
	l=treap.rnk(y);
	treap.split(treap.rt,lft,rht,l);
	treap.rt=treap.merge(treap.merge(lft,mid),rht);	
}

signed main()
{
//	freopen("apple.in","r",stdin);
//	freopen("apple.out","w",stdout);
	srand(time(0));
	memset(head,-1,sizeof(head));
	read(n),read(m);
	Rep(i,1,n)read(a[i]);
	Rep(i,1,n-1){
		int x,y;
		read(x),read(y);
		add(x,y),add(y,x);	
	}
	dfs(1,0);
	Rep(i,1,m){
		int opt,x,y;
		read(opt),read(x);
		if(opt==1)printf("%lld\n",query(x)/2);
		if(opt==2)read(y),update(x,y);
		if(opt==3)read(y),linkcut(x,y);	
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值