zoukankan      html  css  js  c++  java
  • NOI2021部分题目题解

    Day 1

    轻重边

    description

    给出一棵树,初始时每条边都是轻边。有(m) 次操作。操作分如下两种:

    • 给定两点(a,b) ,先对于(a,b) 路径上所有点(x) ,将与(x) 相连的边全部变为轻边,然后再将处于(a,b) 路径上的边变为重边。(修改)
    • 给定两点(a,b) ,询问其路径上有多少条重边。(询问)

    (n,mle 10^5)

    solution

    我们可以从点的方面进行考虑。考虑每次修改时将对应路径上所有点都标记上一个新的时间戳,那么一条边是重边当且仅当其两端点的时间戳相同。(因此初始时需要特殊构造)那么询问可以转化为有多少对相邻点颜色相同,这个可以使用线段树进行维护。于是树链剖分+线段树即可,稍微注意下边界情况。

    复杂度(mathcal O(nlog^2n))

    code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    const int N=1e5+5,inf=0x3f3f3f3f;
    int n,m;vector<int>e[N];
    inline void add(int x,int y){e[x].push_back(y);}
    namespace SGT
    {
    	int ct[N<<2],lc[N<<2],rc[N<<2],tag[N<<2];
    	#define ls (rt<<1)
    	#define rs (rt<<1|1)
    	inline void up(int rt)
    	{
    		ct[rt]=ct[ls]+ct[rs]-(rc[ls]==lc[rs]);
    		lc[rt]=lc[ls],rc[rt]=rc[rs];
    	}
    	inline void cov(int rt,int c){lc[rt]=rc[rt]=c;ct[rt]=1;tag[rt]=c;}
    	inline void dn(int rt)
    	{
    		if(!tag[rt])return;int&tg=tag[rt];
    		cov(ls,tg),cov(rs,tg),tg=0;
    	}
    	void update(int rt,int l,int r,int ql,int qr,int c)
    	{
    		if(ql<=l&&r<=qr)return cov(rt,c);
    		dn(rt);int mid=(l+r)>>1;
    		if(ql<=mid)update(ls,l,mid,ql,qr,c);
    		if(mid<qr)update(rs,mid+1,r,ql,qr,c);
    		up(rt);
    	}
    	int query(int rt,int l,int r,int ql,int qr,int&lcc,int&rcc)
    	{
    		if(ql<=l&&r<=qr)
    		{
    			if(l==ql)lcc=lc[rt];
    			if(r==qr)rcc=rc[rt];
    			return ct[rt];
    		}
    		dn(rt);int mid=(l+r)>>1;
    		if(qr<=mid)return query(ls,l,mid,ql,qr,lcc,rcc);
    		if(mid<ql)return query(rs,mid+1,r,ql,qr,lcc,rcc);
    		int tl=0,tr=0;
    		int r1=query(ls,l,mid,ql,mid,lcc,tl);
    		int r2=query(rs,mid+1,r,mid+1,qr,tr,rcc);
    		return r1+r2-(tl==tr);
    	}
    	void clear(int rt,int l,int r)
    	{
    		ct[rt]=lc[rt]=rc[rt]=tag[rt]=0;
    		if(l==r)return;int mid=(l+r)>>1;
    		clear(ls,l,mid),clear(rs,mid+1,r);
    	}
    	#undef ls
    	#undef rs
    }
    int sz[N],dep[N],son[N],top[N],id[N],tim,fa[N];
    void dfs1(int u,int f)
    {
    	sz[u]=1;dep[u]=dep[f]+1;
    	int mx=0;fa[u]=f;son[u]=0;
    	for(int v:e[u])if(v^f)
    	{
    		dfs1(v,u);sz[u]+=sz[v];
    		if(sz[v]>mx)mx=sz[v],son[u]=v;
    	}
    }
    void dfs2(int u,int tp)
    {
    	id[u]=++tim;top[u]=tp;
    	SGT::update(1,1,n,id[u],id[u],(dep[u]&1)?0:-1);
    	if(son[u])dfs2(son[u],tp);
    	for(int v:e[u])if(v!=fa[u]&&v!=son[u])dfs2(v,v);
    }
    inline int 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]?x:y;
    }
    inline int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
    inline void upline(int x,int y,int t)
    {
    	while(top[x]^top[y])
    	{
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		SGT::update(1,1,n,id[top[x]],id[x],t);
    		x=fa[top[x]];
    	}
    	if(id[x]>id[y])swap(x,y);
    	SGT::update(1,1,n,id[x],id[y],t);
    }
    inline int q(int x,int y)
    {
    	int cx=inf,dx=inf,cy=inf,dy=inf,tmp=inf,ans=0,d=dis(x,y);
    	while(top[x]^top[y])
    	{
    		if(dep[top[x]]<dep[top[y]])
    		{
    			ans+=SGT::query(1,1,n,id[top[y]],id[y],tmp,dy);
    			if(dy==cy)--ans;cy=tmp;
    			y=fa[top[y]];
    		}
    		else
    		{
    			ans+=SGT::query(1,1,n,id[top[x]],id[x],tmp,dx);
    			if(dx==cx)--ans;cx=tmp;
    			x=fa[top[x]];
    		}
    	}
    	if(id[x]>id[y])swap(x,y),swap(cx,cy);
    	ans+=SGT::query(1,1,n,id[x],id[y],dx,dy);
    	if(dx==cx)--ans;if(dy==cy)--ans;
    	return d-ans+1;
    }
    int main()
    {
    	int T=read();
    	while(T-->0)
    	{
    		n=read(),m=read();
    		for(int i=1,u,v;i<n;++i)
    			u=read(),v=read(),add(u,v),add(v,u);
    		dfs1(1,0);dfs2(1,1);
    		for(int i=1;i<=m;++i)
    		{
    			int op=read(),a=read(),b=read();
    			if(op==1)upline(a,b,i);
    			else printf("%d
    ",q(a,b));
    		}
    		for(int i=1;i<=n;++i)e[i].clear();//SGT::clear(1,1,n);
    		tim=0;
    	}
    	return 0;
    }
    

    路径交点

    description

    以这样的方式给出一个图。首先给定层数(k) 以及(n_1,n_2,cdots,n_k) ,满足(n_1=n_k,forall iin[2,k-1],n_iin[n_1,2n_1]) ,表示第(i) 层恰好有(n_i) 个点,编号为(1sim n_i)。而后给出边,所有边都满足起点在第(i) 层而终点在第(i+1) 层,其中(iin[1,k-1]) 。定义一簇路径为(n_1) 条从第一层出发最终到达第(k) 层的路径且满足不存在一个点使得其被超过一条路径经过。

    对于两条路径(P,Q) ,假设它们在第(i) 层和第(i+1) 层之间的边为((P_i,P_{i+1}),(Q_i,Q_{i+1})) ,则我们称二者在第(j) 层后有一个交点当且仅当(P_i-Q_i,P_{i+1}-Q_{i+1}) 异号。定义两条路径交点数为所有层后的交点总数。定义一簇路径的交点数为两两路径交点数之和。

    求偶数个交点数的方案减去及数个交点数的方案对(998244353) 取模后的值。

    (n1le 100,kle 100)

    solution

    不相交路径让我们联想到(LGV) 引理,它是这样描述的,令

    [M=left[egin{matrix}e(A_1,B_1)&e(A_1,B_2)&cdots&e(A_1,B_n)\e(A_2,B_1)&e(A_2,B_2)&cdots&e(A_2,B_n)\vdots&vdots&ddots&vdots\e(A_n,B_1)&e(A_n,B_2)&cdots&e(A_n,B_n)end{matrix} ight] ]

    其中(A_1,A_2,cdots,A_n)(B_1,B_2,cdots,B_n) 分别表示起点和终点,(e(A_i,B_j)) 表示从第(i) 个起点到第(j) 个终点的方案数,那么有

    [det(M)=sum_p(-1)^{N(p)} ]

    其中(p) 表示(1sim n) 的排列,表示一组不相交的路径(S) ,其中(S_i) 为一条从(A_i)(B_{p_i}) 的路径。而(N(p)) 代表排列(p) 的逆序对个数。注意这里只是呈现了该引理的简化形式。

    容易发现题目中所谓的交点其实就是逆序对。因此当(k=2) 时可以直接套用(LGV) 引理。其他情况下,可以考虑两条路径,容易发现增加层数并不会影响其交点个数的奇偶性(具体证明应该类似勘根定理),因此仍然可以套用(LGV) 引理。求路径数量可以采用矩阵乘法。

    复杂度(mathcal O(n^4))

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=105,mod=998244353;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline void inc(int&x,int y){x=add(x,y);}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline void rec(int&x,int y){x=dec(x,y);}
    inline int qpow(int x,int y,int res=1)
    {
    	for(;y;y>>=1,x=1ll*x*x%mod)
    		(y&1)&&(res=1ll*res*x%mod);
    	return res;
    }
    struct Matrix
    {
    	int a,b,c[N<<1][N<<1];
    	inline void pre(int _a,int _b)
    	{a=_a,b=_b;for(int i=1;i<=a;++i)for(int j=1;j<=b;++j)c[i][j]=0;}
    	inline int det()
    	{
    		assert(a==b);
    		int ret=1;
    		for(int i=1;i<=a;++i)
    		{
    			if(!c[i][i])
    				for(int j=i+1;j<=a;++j)
    					if(c[j][i]){swap(c[i],c[j]);ret=mod-ret;break;}
    			if(!c[i][i])return 0;
    			ret=1ll*ret*c[i][i]%mod;
    			int iv=qpow(c[i][i],mod-2);
    			for(int j=i;j<=a;++j)c[i][j]=1ll*c[i][j]*iv%mod;
    			for(int j=i+1;j<=a;++j)
    				if(c[j][i])
    					for(int k=a;k>=i;--k)
    						rec(c[j][k],1ll*c[i][k]*c[j][i]%mod);
    		}
    		return ret;
    	}
    }A[N];
    inline Matrix operator*(const Matrix&x,const Matrix&y)
    {
    	assert(x.b==y.a);
    	Matrix z;z.pre(x.a,y.b);
    	for(int i=1;i<=z.a;++i)
    		for(int j=1;j<=z.b;++j)
    			for(int k=1;k<=x.b;++k)
    				inc(z.c[i][j],1ll*x.c[i][k]*y.c[k][j]%mod);
    	return z;
    }
    int n[N],m[N];
    int main()
    {
    	int T;scanf("%d",&T);
    	while(T-->0)
    	{
    		int k;scanf("%d",&k);
    		for(int i=1;i<=k;++i)scanf("%d",n+i);
    		for(int i=1;i<k;++i)A[i].pre(n[i],n[i+1]);
    		for(int i=1;i<k;++i)scanf("%d",m+i);
    		for(int i=1;i<k;++i)
    			while(m[i]--)
    			{
    				int x,y;scanf("%d%d",&x,&y);
    				A[i].c[x][y]=1;
    			}
    		Matrix T=A[1];
    		for(int i=2;i<k;++i)T=T*A[i];
    		printf("%d
    ",T.det());
    	}
    	return 0;
    }
    

    庆典

    descirption

    给定(n) 个点(m) 条边的有向图(G) ,满足对于任意三个点(x,y,z) ,若从(x)(y)出发均能到达(z) ,那么(x) 能到达(y)(y) 能到达(z) 。现在有(q) 次询问,每次询问给出(s_i,t_i) ,要求从(s_i) 出发经过一些点到达(t_i) 。一个点可被经过多次。同时,每次询问时可以临时给定(k) 条有向边(不一定满足原先图的性质)。现在对于每次询问求出其可能会经过多少个点。

    (n,qle 3 imes 10^5,kle 2)

    solution

    容易想到先将图(G) 进行缩点,因为对于一个强连通分量里的点来说它们的可被经过性是一致的。

    (G) 有奇怪的性质,我们考虑这个性质应该如何使用。若缩点后(x) 能通过一条边就到达(y) ,记(x ightarrow y) 。缩点后原图变为一个(DAG) ,对于点(z) ,若(exist x,y ext{s.t.}x ightarrow z,y ightarrow z) ,那么必然有(x ightarrow y)(y ightarrow x) 。不妨设为(x ightarrow y) ,那么可以发现若删去边(x ightarrow z) 是不会对答案产生影响的。通过这种方法,我们可以将这个(DAG) 转化为一棵外向树。

    由于(kle 2) ,因此我们可以通过分类讨论来回答询问,但这实在是过于繁琐。我们可以换一种方式来刻画:即从(s) 出发可以到达的点和可以到达(t) 的点的交即是所求答案。而从(s) 出发可以到达的点就是一些子树的并,可以到达(t) 的点则是到根的路径并。可以通过暴力容斥或者虚树来求得答案。

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+5;
    int n,m,q,k;
    namespace G1
    {
    	vector<int>e[N];
    	int dfn[N],low[N],tim,id[N],sta[N],top,cnt,num[N];bool insta[N];
    	void dfs(int u)
    	{
    		low[u]=dfn[u]=++tim;
    		sta[++top]=u,insta[u]=1;
    		for(int v:e[u])
    		{
    			if(!dfn[v])
    				dfs(v),low[u]=min(low[u],low[v]);
    			else if(insta[v])low[u]=min(low[u],dfn[v]);
    		}
    		if(low[u]==dfn[u])
    		{
    			++cnt;int v;
    			do
    			{
    				v=sta[top--];insta[v]=0;
    				++num[cnt],id[v]=cnt;
    			}while(v!=u);
    		}
    	}
    	inline void main()
    	{
    		for(int i=1,u,v;i<=m;++i)
    			scan(u),scan(v),e[u].push_back(v);
    		for(int i=1;i<=n;++i)if(!dfn[i])dfs(i);
    	}
    }
    using G1::id;using G1::cnt;using G1::num;
    namespace G2
    {
    	vector<int>G[N];int fa[N],in[N],out[N],tim,s[N],rt,a[5],b[5],pa[N][20],dep[N];
    	void dfs(int u)
    	{
    		in[u]=++tim;dep[u]=dep[fa[u]]+1;
    		s[u]=num[u]+s[fa[u]];
    		pa[u][0]=fa[u];
    		for(int i=1;;++i)
    		{
    			pa[u][i]=pa[pa[u][i-1]][i-1];
    			if(!pa[u][i])break;
    		}
    		for(int v:G[u])dfs(v);
    		out[u]=++tim;
    	}
    	inline bool isac(int x,int y){return in[x]<=in[y]&&out[y]<=out[x];}
    	inline int lca(int x,int y)
    	{
    		if(dep[x]>dep[y])swap(x,y);
    		if(isac(x,y))return x;
    		for(int i=18;~i;--i)
    			if(pa[x][i]&&!isac(pa[x][i],y))
    				x=pa[x][i];
    		return fa[x];
    	}
    	set<int>tx,ty;int tmp[5];
    	inline void ins1(int u)
    	{
    		int ut=0;
    		for(auto v:tx)
    			if(isac(u,v))tmp[++ut]=v;
    			else if(isac(v,u))return;
    		for(int i=1;i<=ut;++i)tx.erase(tmp[i]);
    		tx.insert(u);
    	}
    	inline void ins2(int u)
    	{
    		int ut=0;
    		for(auto v:ty)
    			if(isac(u,v))return;
    			else if(isac(v,u))tmp[++ut]=v;
    		for(int i=1;i<=ut;++i)ty.erase(tmp[i]);
    		ty.insert(u);
    	}
    	inline int work(int u)
    	{
    		int ret=0;
    		for(auto v:tx)
    			ret+=isac(v,u)?s[u]-s[fa[v]]:0;
    		return ret;
    	}
    	inline int solve(int u,int v)
    	{
    		tx.clear(),ty.clear();
    		tx.insert(u),ty.insert(v);
    		int t=k;
    		while(t--)
    			for(int i=1;i<=k;++i)
    			{
    				bool flag=0;
    				for(auto v:tx)
    					if(isac(v,a[i])){flag=1;break;}
    				if(flag)ins1(b[i]);flag=0;
    				for(auto v:ty)
    					if(isac(b[i],v)){flag=1;break;}
    				if(flag)ins2(a[i]);
    			}
    		int ans=0,st=1<<ty.size(),ut=0;
    		for(auto v:ty)tmp[ut++]=v;
    		for(int i=1;i<st;++i)
    		{
    			int ret=0,tt=0;
    			for(int o=0;o<ut;++o)
    				if(i&(1<<o))ret=!ret?tmp[o]:lca(ret,tmp[o]),++tt;
    			ret=work(ret);
    			ans+=(tt&1)?-ret:ret;
    		}
    		return ans<0?-ans:ans;
    	}
    	inline void main()
    	{
    		for(int u=1;u<=n;++u)
    			for(int v:G1::e[u])
    				if(id[u]^id[v])
    					G[id[v]].push_back(id[u]);
    		for(int i=1;i<=cnt;++i)
    		{
    			if(G[i].empty()){rt=i;continue;}
    			fa[i]=*min_element(G[i].begin(),G[i].end());
    		}
    		for(int i=1;i<=cnt;++i)G[i].clear();
    		for(int i=1;i<=cnt;++i)if(rt^i)G[fa[i]].push_back(i);
    		dfs(rt);
    		for(int d=1;d<=q;++d)
    		{
    			int s,t;scan(s),scan(t);s=id[s],t=id[t];
    			for(int i=1;i<=k;++i)scan(a[i]),scan(b[i]),a[i]=id[a[i]],b[i]=id[b[i]];
    			putint(solve(s,t),'
    ');
    		}
    	}
    }
    int main()
    {
    	scan(n),scan(m),scan(q),scan(k);
    	G1::main();G2::main();flush();
    	return 0;
    }
    

    Day 2

    量子通信

    给出(n) 个长度为(256)(01) 串,同时有(q) 组询问,每组询问给定长度为(256)(01) 串和(k) ,表示询问在给出的(n) 个串中是否存在一个串使得其与询问串只有至多(k) 位不同。强制在线了个寂寞。

    (nle 4 imes 10^5,qle 1.2 imes 10^5,kle 16)

    solution

    注意到(kle 15) ,于是我们便想在(k) 上面做文章以优化暴力比较。

    我们可以将长度为(256)(01) 串分割为(16) 个子串,每个子串长度为(16) 。根据抽屉原理,若某个询问串满足条件,那么必然有一个子串是相同的。

    发现数据随机,于是我们可以使用vector记下第(i) 个子串为(j) 的原串的编号。这样询问时就可以枚举是第几个子串相同进而一一枚举进行比较。通过预处理可以(mathcal O(1)) 得到和某个子串相差多少位。这样下来平均运算次数为(mcdot 16 imes dfrac n{2^{16}}cdot 16approx 2 imes 10^8) 。稍微注意下常数即可通过。

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    const int N = 400005;
    bool s[N][256],t[256];int ss[N][16],tt[16];
    inline ull myRand(ull &k1, ull &k2)
    {
        ull k3 = k1, k4 = k2;
        k1 = k4;
        k3 ^= (k3 << 23);
        k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
        return k2 + k4;
    }
    inline void gen(int n, ull a1, ull a2) 
    {
        for (int i = 1; i <= n; i++)
            for (int j = 0; j < 256; j++)
                s[i][j]=(myRand(a1, a2) & (1ull << 32)) ? 1 : 0;
    }
    char ch[1<<6];bool lans;
    inline void read()
    {
    	scanf("%s",ch);
    	for(int i=0;i<64;++i)
    	{
    		char c=ch[i];
    		if(c>='A')c-='A'-10;
    		else c^='0';
    		for(int j=((i+1)<<2)-1,o=0;o<4;--j,++o)
    			t[j]=(c>>o)&1;
    	}
    	if(lans)for(int i=0;i<256;++i)t[i]^=1;
    }
    inline void trans(bool a[],int b[])
    {
    	for(int i=0;i<16;++i)
    	{
    		int&ret=b[i];ret=0;
    		for(int j=i<<4,c=0;c<16;++j,++c)
    			ret|=a[j]<<c;
    	}
    }
    int n,m,cnt[1<<16];ull a1,a2;
    vector<int>ht[1<<4][1<<16];
    int main()
    {
    //	freopen("qi1.in","r",stdin);
    	scanf("%d%d%llu%llu",&n,&m,&a1,&a2);
    	gen(n,a1,a2);
    	for(int i=1;i<=n;++i)trans(s[i],ss[i]);
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<16;++j)
    			ht[j][ss[i][j]].push_back(i);
    	cnt[0]=0;for(int i=1;i<65536;++i)cnt[i]=cnt[i^(i&(-i))]+1;
    	while(m--)
    	{
    		read();int k;scanf("%d",&k);
    		trans(t,tt);bool flag=0;
    		for(int i=0;i<16&&!flag;++i)
    			for(int c:ht[i][tt[i]])
    			{
    				int res=0;
    				for(int j=0;j<16;++j)
    				{
    					res+=cnt[tt[j]^ss[c][j]];
    					if(res>k)break;
    				}
    				if(res<=k){flag=1;break;}
    			}
    		lans=flag;puts(flag?"1":"0");
    	}
    	return 0;
    }
    

    密码箱

    description

    solution

    容易发现只要按照题意进行计算分子分母始终互质,因此在以下的讨论中不用关心这一点。

    注意到本题要同时求出分母和分子,这意味着我们需要同时维护这两个东西。可以发现题目中的操作其实就是关于分子分母的线性变换。若用(egin{bmatrix}x\yend{bmatrix}) 表示(dfrac xy) ,考虑在其前面的数为(a_i) ,那么由题知(a_i+dfrac 1{frac xy}=dfrac{a_ix+y}x) ,有(egin{bmatrix}a_i&1\1&0end{bmatrix}egin{bmatrix}x\yend{bmatrix}=egin{bmatrix}a_ix+y\xend{bmatrix}) ,即左乘一个(egin{bmatrix}a_i&1\1&0end{bmatrix}) 即可。因此对于数列(a_0,a_1,cdots,a_k) ,答案就是:

    [ans=(prod_{i=0}^kegin{bmatrix}a_i&1\1&0end{bmatrix})cdotegin{bmatrix}1\0end{bmatrix} ]

    现在的关键就是维护这些矩阵的积了。

    对于W操作,需要给最后一项加一。注意到我们有(egin{bmatrix}a_i&1\1&0end{bmatrix}egin{bmatrix}1&0\k&1end{bmatrix}=egin{bmatrix}a_i+k&1\1&0end{bmatrix}) ,因此直接右乘一个(egin{bmatrix}1&0\1&1end{bmatrix}) 即可。

    对于E操作,分情况进行讨论。若最后两项为(t,1) ,那么第一种变化后为(t+1,1) ,第二种变化为(t,0,1,1) ,这两种变化后的结果分别为:(t+1+dfrac 1{1+frac 1{frac xy}}=dfrac{(t+2)x+(t+1)y}{x+y})(t+dfrac 1{0+frac 1{1+frac 1{1+frac xy}}}=dfrac{(t+2)x+(t+1)y}{x+y}) ,完全一致。因此我们可以只考虑第二种情况,那么相当于右乘的矩阵为:(egin{bmatrix}1&0\-1&1end{bmatrix}egin{bmatrix}1&1\1&0end{bmatrix}egin{bmatrix}1&1\1&0end{bmatrix}=egin{bmatrix}2&1\-1&0end{bmatrix})

    两种操作分别对应于右乘一个矩阵。题目中的APPEND操作显然是易于维护的。至于FLIPREVERSE操作则只需分别维护未FLIP且未REVERSEFLIP且未REVERSE,未FLIPREVERSE以及FLIPREVERSE这四种情况下对应的矩阵即可。可以使用Treap轻松维护。

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5,mod=998244353;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    namespace Treap
    {
    	struct mt{int a,b,c,d;inline void pre(){a=b=c=d=0;}};
    	inline mt operator*(const mt&x,const mt&y)
    	{
    		mt z;z.pre();
    		z.a=add(1ll*x.a*y.a%mod,1ll*x.b*y.c%mod);
    		z.b=add(1ll*x.a*y.b%mod,1ll*x.b*y.d%mod);
    		z.c=add(1ll*x.c*y.a%mod,1ll*x.d*y.c%mod);
    		z.d=add(1ll*x.c*y.b%mod,1ll*x.d*y.d%mod);
    		return z;
    	}
    	const mt M[2]={{1,0,1,1},{2,1,mod-1,0}};
    	mt19937 rd(time(0));
    	unsigned int fix[N];int tot,rt,sz[N],ls[N],rs[N];
    	mt A0[N],A1[N],B0[N],B1[N];bool rev[N],fip[N],vt[N];
    	inline int nd(bool tp)
    	{
    		int p=++tot;sz[p]=1;ls[p]=rs[p]=0;fix[p]=rd();
    		A0[p]=A1[p]=M[tp],B0[p]=B1[p]=M[tp^1];
    		rev[p]=fip[p]=0;vt[p]=tp;
    		return p;
    	}
    	inline void up(int u)
    	{
    		sz[u]=sz[ls[u]]+1+sz[rs[u]];
    		bool t=vt[u];
    		if(ls[u]&&rs[u])
    			A0[u]=A0[ls[u]]*M[t]*A0[rs[u]],A1[u]=A1[rs[u]]*M[t]*A1[ls[u]],
    			B0[u]=B0[ls[u]]*M[t^1]*B0[rs[u]],B1[u]=B1[rs[u]]*M[t^1]*B1[ls[u]];
    		else if(ls[u])
    			A0[u]=A0[ls[u]]*M[t],A1[u]=M[t]*A1[ls[u]],
    			B0[u]=B0[ls[u]]*M[t^1],B1[u]=M[t^1]*B1[ls[u]];
    		else if(rs[u])
    			A0[u]=M[t]*A0[rs[u]],A1[u]=A1[rs[u]]*M[t],
    			B0[u]=M[t^1]*B0[rs[u]],B1[u]=B1[rs[u]]*M[t^1];
    		else A0[u]=A1[u]=M[t],B0[u]=B1[u]=M[t^1];
    	}
    	inline void cov1(int u)
    	{
    		swap(ls[u],rs[u]);
    		swap(A0[u],A1[u]),swap(B0[u],B1[u]);
    		rev[u]^=1;
    	}
    	inline void cov2(int u)
    	{
    		swap(A0[u],B0[u]),swap(A1[u],B1[u]);
    		vt[u]^=1;fip[u]^=1;
    	}
    	inline void dn(int u)
    	{
    		if(rev[u])
    		{
    			if(ls[u])cov1(ls[u]);
    			if(rs[u])cov1(rs[u]);
    			rev[u]=0;
    		}
    		if(fip[u])
    		{
    			if(ls[u])cov2(ls[u]);
    			if(rs[u])cov2(rs[u]);
    			fip[u]=0;
    		}
    	}
    	void split(int p,int d,int&l,int&r)
    	{
    		if(!p)return l=r=0,void();
    		dn(p);
    		if(sz[ls[p]]+1<=d)
    			l=p,split(rs[p],d-1-sz[ls[p]],rs[l],r),up(l);
    		else r=p,split(ls[p],d,l,ls[r]),up(r);
    	}
    	int merge(int x,int y)
    	{
    		if(!x||!y)return x|y;
    		dn(x),dn(y);
    		if(fix[x]>fix[y])
    		{
    			rs[x]=merge(rs[x],y);
    			up(x);return x;
    		}
    		else
    		{
    			ls[y]=merge(x,ls[y]);
    			up(y);return y;
    		}
    	}
    	inline void ins(bool tp){rt=merge(rt,nd(tp));}
    	inline void grev(int l,int r)
    	{
    		int a,b,c;
    		split(rt,l-1,a,b),split(b,r-l+1,b,c);
    		cov1(b);rt=merge(merge(a,b),c); 
    	}
    	inline void gfip(int l,int r)
    	{
    		int a,b,c;
    		split(rt,l-1,a,b),split(b,r-l+1,b,c);
    		cov2(b);rt=merge(merge(a,b),c);
    	}
    	inline void print(){printf("%d %d
    ",A0[rt].a,A0[rt].c);}
    }
    using Treap::ins;
    using Treap::grev;
    using Treap::gfip;
    using Treap::print;
    char ch[N];
    inline bool id(char c){return c=='E';}
    int main()
    {
    	int n,q;scanf("%d%d",&n,&q);
    	scanf("%s",ch+1);ins(0);
    	for(int i=1;i<=n;++i)ins(id(ch[i]));
    	print();
    	while(q--)
    	{
    		scanf("%s",ch);
    		if(ch[0]=='A')
    			scanf("%s",ch),ins(id(ch[0]));
    		else
    		{
    			int l,r;scanf("%d%d",&l,&r);++l,++r;
    			if(ch[0]=='F')gfip(l,r);
    			else grev(l,r);
    		}
    		print();
    	}
    	return 0;
    }
    
    NO PAIN NO GAIN
  • 相关阅读:
    三种空格unicode(u00A0,u0020,u3000)表示的区别
    python调用C++之pybind11入门(相互调用)
    基于go手动写个转发代理服务
    git rebase VS git merge
    外挂
    C#本地修改器
    C# 人工智能开源库生物特征
    深层信念网络
    ASP.NET CORE(C#)与Spring Boot MVC(JAVA)
    Net UI Spy工具:ManagedSpy
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/15176910.html
Copyright © 2011-2022 走看看