Codeforces Round #539 Div1 题解

Codeforces Round #539 Div1 题解

听说这场很适合上分QwQ
然而太晚了QaQ

A. Sasha and a Bit of Relax

翻译

有一个长度为(n)的数组,问有多少个长度为偶数的连续区间,使得其前一半的异或和等于后一半的异或和。

题解

显然就是求长度为偶数且异或和为(0)的区间个数
求异或和为(0)的区间个数很简单,对于整个区间求异或前缀和看看有多少个相等就好了。
求长度为偶数的也很简单,把每个位置的异或前缀和按照位置的奇偶性分开求个数每次计算一下后面和当前位置的异或前缀和相等的个数就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 300300
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,a[MAX];
int s[2][1<<20];
ll ans;
int main()
{
	n=read();
	for(int i=1;i<=n;++i)a[i]=read()^a[i-1],s[i&1][a[i]]+=1;
	for(int i=1;i<=n;++i)ans+=s[i&1^1][a[i-1]],s[i&1][a[i]]-=1;
	cout<<ans<<endl;
	return 0;
}

B. Sasha and One More Name

翻译

给定一个回文串,你可以把它分成若干段,然后随意的交换他们的顺序,使得最终得到一个和原串不同的回文串。求分段的最小次数。

题解

首先不合法一定是除了回文中心外,所有字符都相同的串。
只有有解的话,两次操作即可完成。即找到一个包含两个以上不同字符的非回文串前缀,然后把前缀后缀的这个串交换。
现在考虑什么情况下只需要一次操作,那么枚举一个断点,交换一下,检查是否是回文串即可。这个因为串长很小直接暴力就行了。(我哈希自然溢出被卡了嘤嘤嘤)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
#define MAX 5050
int n;char s[MAX],ch[MAX];
void check0()
{
	for(int i=1;i<=n/2;++i)if(s[i]!=s[1])return;
	puts("Impossible");exit(0);
}
void check1()
{
	for(int i=1;i<n;++i)
	{
		int len=0;bool fl=true;
		for(int j=i+1;j<=n;++j)ch[++len]=s[j];
		for(int j=1;j<=i;++j)ch[++len]=s[j];
		for(int j=1;j<=n;++j)if(ch[j]!=s[j]){fl=false;break;}
		if(fl)continue;
		for(int j=1;j<=n/2;++j)if(ch[j]!=ch[n-j+1]){fl=true;break;}
		if(!fl)puts("1"),exit(0);
	}
}
int main()
{
	scanf("%s",s+1);n=strlen(s+1);
	check0();check1();puts("2");
	return 0;
}

C. Sasha and a Patient Friend

翻译

有一只鸽子有一颗强大的内心,然而内心中的鸽值却并不是无限的。
一开始它的内心有(V)单位鸽值,然后它的内心中有一个水龙头,会每个单位时间向外流出(s)单位鸽值((s)可以为负数,会改变,初始时(s=0)),当这只鸽子没有鸽值的时候它就会彻底咕咕咕。
现在你是(zsy),你进行了(Q)次操作,操作有以下几种:

  • 从第(t)时刻开始,把水龙头的速度修改为(s)
  • 删除从(t)时刻开始的修改操作
  • (l)时刻作为初始时刻,打开这只鸽子内心的水龙头,在(r)时刻关上。回答这只鸽子是否会咕咕咕,如果会 回答在什么时刻咕咕咕(注意这个时间可能是小数),如果不会输出(-1)。询问之间独立。

保证在操作进行的过程中,任意相同时刻不会同时出现两次修改操作。

题解

不要在意辣鸡翻译。

显然要把所有的修改按照时间排个序之类的操作,所以掏出了平衡树。
对于每个节点,维护其这个修改维持的时间以及流出的估值的数量,维护区间内每个修改结束时流出的最大值,这样子单次询问只需要在平衡树上二分就好了。
看代码吧。。。(代码有点乱)

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (t[x].ch[0])
#define rson (t[x].ch[1])
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Data{int k,t;ll s,mxs;};
Data operator+(Data a,Data b)
{
	Data c;c.k=b.k;
	c.s=a.s+b.s;c.t=a.t+b.t;
	c.mxs=max(a.mxs,b.mxs+a.s);
	return c;
}
struct Node
{
	int ff,ch[2];
	int k,t,len;
	Data a;
	Data Pre(){return (Data){k,len,1ll*k*len,max(1ll*k*len,0ll)};}
}t[MAX];
int rt,tot;
void pushup(int x){t[x].a=t[lson].a+t[x].Pre()+t[rson].a;}
void rotate(int x)
{
	int y=t[x].ff,z=t[y].ff;
	int k=t[y].ch[1]==x;
	if(z)t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
	t[y].ch[k]=t[x].ch[k^1];if(t[x].ch[k^1])t[t[x].ch[k^1]].ff=y;
	t[x].ch[k^1]=y;t[y].ff=x;
	pushup(y);pushup(x);
}
void Splay(int x,int goal)
{
	while(t[x].ff!=goal)
	{
		int y=t[x].ff,z=t[y].ff;
		if(z!=goal)
			(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
		rotate(x);
	}
	if(!goal)rt=x;
}
void Insert(int a,int s)
{
	int x=rt,f=0;
	while(233)
	{
		if(t[x].t>a)f=x,x=lson;
		else f=x,x=rson;
		if(!x)break;
	}
	int k=t[f].t<a;x=++tot;
	if(f)t[f].ch[k]=x;t[x].ff=f;
	t[x].t=a;t[x].k=s;Splay(x,0);
}
int Next(int a,int p)
{
	int x=rt,u=0;bool fl=false;
	while(233)
	{
		if(t[x].t==a){fl=true;break;}
		int k=t[x].t<a;
		if(p^k)u=x;
		if(!t[x].ch[k])break;
		x=t[x].ch[k];
	}
	if(!fl)return u;
	if(!t[x].ch[p])return u;
	else x=t[x].ch[p];
	while(t[x].ch[p^1])x=t[x].ch[p^1];
	return x;
}
void Adding(int a,int s)
{
	Insert(a,s);
	int nw=tot,lt=Next(a,0),nt=Next(a,1);
	Splay(lt,0);t[lt].len=a-t[lt].t;pushup(lt);
	Splay(nw,0);t[nw].len=t[nt].t-a;pushup(nw);
}
void Delete(int a)
{
	int uL=Next(a,0),uR=Next(a,1);
	Splay(uL,0);Splay(uR,uL);
	t[uR].ch[0]=0;
	pushup(uR);
	t[uL].len=t[uR].t-t[uL].t;
	pushup(uL);
}
double Query(int l,int r,ll V)
{
	if(!V)return l;
	int uL=Next(l,0),uR=Next(r,1),QwQ;
	Splay(uL,0);Splay(uR,uL);
	int nw=t[uR].ch[0];QwQ=nw;
	if(t[nw].a.mxs<V)return -1;
	while(t[QwQ].ch[0])QwQ=t[QwQ].ch[0];
	int u=nw;Data now=(Data){0,0,0,0};
	while(233)
	{
		Data pre=now+t[t[u].ch[0]].a+t[u].Pre();
		if(pre.mxs<V)now=pre,u=t[u].ch[1];
		else
		{
			Data qwq=now+t[t[u].ch[0]].a;
			if(qwq.mxs<V)return 1.0*(V-qwq.s)/t[u].k+t[u].t;
			u=t[u].ch[0];
		}
		if(!u)return -1;
	}
}
int main()
{
	Insert(0,0);Insert(2e9+1,0);
	int Q=read();
	while(Q--)
	{
		int opt=read();
		if(opt==1)
		{
			int a=read(),s=read();
			Adding(a,-s);
		}
		else if(opt==2)Delete(read());
		else
		{
			int l=read(),r=read(),V=read();
			double s=Query(l,r,V);
			if(s>r)puts("-1");
			else printf("%.10lf
",s);
		}
	}
	return 0;
}

D. Sasha and Interesting Fact from Graph Theory

翻译

有一个(n)个节点的树,边权是([1,m])中的任意整数,现在问你有多少棵树满足(a,b)之间的路径权值和为(m)

题解

首先枚举(a,b)之间的点数(i),这里选出(i)个点然后连成一条链,方案数是({n-2choose i}*i!)
然后把权值和(m)分配到这(i+1)条边上,方案数是({m-1choose i})
然后剩下的点任意连上来形成树,方案数是(n^{n-i-3}*(i+2))
然后剩下的边的权值任意,方案数是(m^{n-2-i})
加起来就完了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 1000000007
#define MAX 2000100
int n,m,a,b,ans,jc[MAX],jv[MAX];
int fpow(int a,int b)
{
	int s=1;if(b<0)b+=MOD-1;
	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
	return s;
}
int C(int n,int m){if(n<m||n<0||m<0)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int main()
{
	scanf("%d%d%d%d",&n,&m,&a,&b);
	jc[0]=jv[0]=jv[1]=1;
	for(int i=2;i<=n+m;++i)jv[i]=1ll*jv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n+m;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=1;i<=n+m;++i)jv[i]=1ll*jv[i-1]*jv[i]%MOD;
	for(int i=0;i<=n-2;++i)
		ans=(ans+1ll*C(n-2,i)*jc[i]%MOD*C(m-1,i)%MOD*fpow(n,n-i-3)%MOD*(i+2)%MOD*fpow(m,n-2-i))%MOD;
	printf("%d
",ans);
	return 0;
}

E. Sasha and a Very Easy Test

翻译

维护一个数列,支持以下操作

  • 区间乘法
  • 单点除法,保证能够整除
  • 模意义下的区间和

模数提前给定,不保证是质数。

题解

因为模数不保证是质数,所以现在的问题在于怎么单点除法。
其实还是很呆。
把模数质因数分解,维护两棵线段树,一棵记录原数,一棵记录去除所有模数的质因子后的值。
每次区间乘法两棵同时改。
现在的问题在于区间除法,再开一个(BIT)记录区间每个模数的质因子被乘的次数,这样子就可以计算除完后的每个质因子的个数,然后两棵线段树分别修改即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int fpow(int a,int b,int MOD){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
void exgcd(int a,int b,int &x,int &y){if(!b){x=1;y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
int Inv(int a,int MOD){int x,y;exgcd(a,MOD,x,y);x=(x%MOD+MOD)%MOD;return x;}
int n,MOD,Q,a[MAX],fac[10],tot;
int p[10][MAX];
struct SegMentTree
{
	int t[MAX<<2],tag[MAX<<2];
	void Build(int now,int l,int r)
	{
		tag[now]=1;
		if(l==r){t[now]=a[l]%MOD;return;}
		int mid=(l+r)>>1;
		Build(lson,l,mid);Build(rson,mid+1,r);
		t[now]=(t[lson]+t[rson])%MOD;
	}
	void puttag(int now,int w){t[now]=1ll*t[now]*w%MOD;tag[now]=1ll*tag[now]*w%MOD;}
	void pushdown(int now)
	{
		if(tag[now]==1)return;
		puttag(lson,tag[now]);puttag(rson,tag[now]);
		tag[now]=1;
	}
	void Modify(int now,int l,int r,int p,int w)
	{
		if(l==r){t[now]=w;return;}
		int mid=(l+r)>>1;pushdown(now);
		if(p<=mid)Modify(lson,l,mid,p,w);
		else Modify(rson,mid+1,r,p,w);
		t[now]=(t[lson]+t[rson])%MOD;
	}
	void Modify_mul(int now,int l,int r,int L,int R,int w)
	{
		if(L<=l&&r<=R){puttag(now,w);return;}
		int mid=(l+r)>>1;pushdown(now);
		if(L<=mid)Modify_mul(lson,l,mid,L,R,w);
		if(R>mid)Modify_mul(rson,mid+1,r,L,R,w);
		t[now]=(t[lson]+t[rson])%MOD;
	}
	int Query(int now,int l,int r,int L,int R)
	{
		if(L<=l&&r<=R)return t[now];
		int mid=(l+r)>>1,ret=0;pushdown(now);
		if(L<=mid)ret=(ret+Query(lson,l,mid,L,R))%MOD;
		if(R>mid)ret=(ret+Query(rson,mid+1,r,L,R))%MOD;
		return ret;
	}
}T1,T2;
int lb(int x){return x&(-x);}
struct BIT
{
	int c[MAX];
	void add(int x,int w){while(x<=n)c[x]+=w,x+=lb(x);}
	int getsum(int x){int s=0;while(x)s+=c[x],x-=lb(x);return s;}
	void Modify(int l,int r,int w){add(l,w);add(r+1,-w);}
}T[10];
int Div[10];
int Fact(int x)
{
	for(int i=1;i<=tot;++i)Div[i]=0;
	for(int i=1;i<=tot;++i)
		while(x%fac[i]==0)Div[i]+=1,x/=fac[i];
	return x;
}
int main()
{
	n=read();MOD=read();
	for(int i=1;i<=n;++i)a[i]=read();
	int P=MOD;
	for(int i=2;i*i<=MOD;++i)
		if(P%i==0){fac[++tot]=i;while(P%i==0)P/=i;}
	if(P>1)fac[++tot]=P;
	T1.Build(1,1,n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=tot;++j)
			while(a[i]%fac[j]==0)++p[j][i],a[i]/=fac[j];
	T2.Build(1,1,n);
	Q=read();
	while(Q--)
	{
		int opt=read();
		if(opt==1)
		{
			int l=read(),r=read(),x=read();
			T2.Modify_mul(1,1,n,l,r,Fact(x)%MOD);
			for(int i=1;i<=tot;++i)T[i].Modify(l,r,Div[i]);
			T1.Modify_mul(1,1,n,l,r,x%MOD);
		}
		else if(opt==2)
		{
			int l=read(),x=read(),c=Fact(x);
			for(int i=1;i<=tot;++i)p[i][l]-=Div[i];
			for(int i=1;i<=tot;++i)Div[i]=p[i][l]+T[i].getsum(l);
			c=1ll*T2.Query(1,1,n,l,l)*Inv(c,MOD)%MOD;
			T2.Modify(1,1,n,l,c);
			for(int i=1;i<=tot;++i)c=1ll*c*fpow(fac[i],Div[i],MOD)%MOD;
			T1.Modify(1,1,n,l,c);
		}
		else
		{
			int l=read(),r=read();
			printf("%d
",T1.Query(1,1,n,l,r));
		}
	}
	return 0;
}

F. Sasha and Algorithm of Silence's Sounds

翻译

有一个(n*m)的网格图,每个格子里填着([1,n*m])中的某个数,每个数恰好出现一次。
现在问有多少个([l,r]),满足这些数字所在的格子恰好构成了一棵树(即一个联通块,且无环)

题解

首先可以对于每个(r),用(LCT)维护出其最小的(L_r),满足([L_r,r])构成森林。
那么现在的问题就是找到(lin[L_r,r]),使得([l,r])构成了一个联通块。
(s[i])表示([i,r])区间内点构成的联通块数量,显然在构成森林的情况下,联通块个数等于点数减去边数,那么我们可以用线段树维护区间减法来维护点数减去边数,这样子只需要统计(1)的个数,因为(1)必定是最小值,所以计算最小值以及最小值个数就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define ls (t[x].ch[0])
#define rs (t[x].ch[1])
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Node{int ff,ch[2],rev;}t[200200];
bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
void rotate(int x)
{
	int y=t[x].ff,z=t[y].ff;
	int k=t[y].ch[1]==x;
	if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
	t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
	t[x].ch[k^1]=y;t[y].ff=x;
}
void pushdown(int x){if(t[x].rev)t[ls].rev^=1,t[rs].rev^=1,swap(ls,rs),t[x].rev^=1;}
int S[200200],top;
void Splay(int x)
{
	S[top=1]=x;
	for(int i=x;!isroot(i);i=t[i].ff)S[++top]=t[i].ff;
	while(top)pushdown(S[top--]);
	while(!isroot(x))
	{
		int y=t[x].ff,z=t[y].ff;
		if(!isroot(y))
			(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
		rotate(x);
	}
}
void access(int x){for(int y=0;x;y=x,x=t[x].ff)Splay(x),rs=y;}
void makeroot(int x){access(x);Splay(x);t[x].rev^=1;}
void split(int x,int y){makeroot(x);access(y);Splay(y);}
void link(int x,int y){makeroot(x);t[x].ff=y;}
void cut(int x,int y){split(x,y);t[y].ch[0]=t[x].ff=0;}
int findroot(int x){access(x);Splay(x);while(ls)x=ls;return x;}
int n,m;ll ans;
int a[1010][1010];
int X[200200],Y[200200];
int d[4][2]={1,0,0,1,-1,0,0,-1};
struct SegMent
{
#define lson (now<<1)
#define rson (now<<1|1)
	struct Node{int v,s;}t[200200<<2];int tag[200200<<2];
	Node Merge(Node a,Node b)
	{
		Node c;
		c.v=min(a.v,b.v);c.s=0;
		if(c.v==a.v)c.s+=a.s;
		if(c.v==b.v)c.s+=b.s;
		return c;
	}
	void pushup(int now){t[now]=Merge(t[lson],t[rson]);}
	void Build(int now,int l,int r)
	{
		if(l==r){t[now]=(Node){1,1};return;}
		int mid=(l+r)>>1;
		Build(lson,l,mid);Build(rson,mid+1,r);
		pushup(now);
	}
	void puttag(int now,int w){t[now].v+=w;tag[now]+=w;}
	void pushdown(int now){puttag(lson,tag[now]);puttag(rson,tag[now]);tag[now]=0;}
	void Modify(int now,int l,int r,int L,int R,int w)
	{
		if(L<=l&&r<=R){puttag(now,w);return;}
		int mid=(l+r)>>1;pushdown(now);
		if(L<=mid)Modify(lson,l,mid,L,R,w);
		if(R>mid)Modify(rson,mid+1,r,L,R,w);
		pushup(now);
	}
	Node Query(int now,int l,int r,int L,int R)
	{
		if(L==l&&r==R)return t[now];
		int mid=(l+r)>>1;pushdown(now);
		if(R<=mid)return Query(lson,l,mid,L,R);
		if(L>mid)return Query(rson,mid+1,r,L,R);
		return Merge(Query(lson,l,mid,L,mid),Query(rson,mid+1,r,mid+1,R));
	}
	int Query(int l,int r){Node a=Query(1,1,n*m,l,r);return a.v==1?a.s:0;}
}T;
bool check(int l,int r,int p)
{
	for(int i=0;i<4;++i)
	{
		int x=X[p]+d[i][0],y=Y[p]+d[i][1];
		if(x<0||y<0||x>n||y>m)continue;
		if(!(l<=a[x][y]&&a[x][y]<=r))continue;
		for(int j=i+1;j<4;++j)
		{
			int xx=X[p]+d[j][0],yy=Y[p]+d[j][1];
			if(xx<0||yy<0||xx>n||yy>m)continue;
			if(!(l<=a[xx][yy]&&a[xx][yy]<=r))continue;
			if(findroot(a[x][y])==findroot(a[xx][yy]))return false;
		}
	}
	return true;
}
void Work(int l,int r,int p,int opt)
{
	for(int i=0;i<4;++i)
	{
		int x=X[p]+d[i][0],y=Y[p]+d[i][1];
		if(x<0||y<0||x>n||y>m)continue;
		if(!(l<=a[x][y]&&a[x][y]<=r))continue;
		if(opt==-1)cut(p,a[x][y]);
		else link(p,a[x][y]),T.Modify(1,1,n*m,1,a[x][y],-1);
	}
}
int main()
{
	n=read();m=read();T.Build(1,1,n*m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)a[i][j]=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)X[a[i][j]]=i,Y[a[i][j]]=j;
	for(int i=1,p=1;i<=n*m;++i)
	{
		while(!check(p,i-1,i))Work(p+1,i-1,p,-1),++p;
		Work(p,i-1,i,1);
		ans+=T.Query(p,i);
		T.Modify(1,1,n*m,p,i,1);
	}
	printf("%I64d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/cjyyb/p/10397504.html