我是靠谱客的博主 甜美月光,这篇文章主要介绍AGC045部分题解,现在分享给大家,希望可以做个参考。

比赛传送门

太菜了,赛时只能搞个一题……

T1

假如每个 S i = 1 S_i=1 Si=1 处的 A i A_i Ai 都可以被后面的 S j = 0 S_j=0 Sj=0 A j A_j Aj 们异或得到,那么最后就一定可以变成 0 0 0,否则不能,因为如果满足这样的条件的话,无论 1 1 1 选手是否异或 A i A_i Ai 0 0 0 选手都能够有办法抵消 A i A_i Ai 的贡献。

实现的话用线性基判断一下就好了,这题可以做到 O ( n ) O(n) O(n),但是 n n n 不知为什么这么小所以就随便写了个 O ( n 2 ) O(n^2) O(n2) 的。

代码如下:

#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
 
namespace my
{
	//-----------------------------------------------------------------------------
	inline char cn()
	{
		static char buf[1000010],*p1=buf,*p2=buf;
		return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
	}
	#define cn getchar
	template<class TY>void read(TY &x)
	{
		x=0;int f1=1;char ch=cn();
		while(ch<'0'||ch>'9'){if(ch=='-')f1=-1;ch=cn();}
		while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=cn(); x*=f1;
	}
	char OP[1000030],Zhan[30];int op=0,Top;
	void fcheck(int type=0){if(type||op>=1000000)fwrite(OP,1,op,stdout),op=0;}
	template<class TY>inline void write(TY x)
	{
		if(x==0){OP[op++]='0';fcheck();return;}
		Top=0;while(x)Zhan[++Top]=x%10+'0',x/=10;
		while(Top)OP[op++]=Zhan[Top--];fcheck();
	}
	#define K_G OP[op++]=' '
	//----------------------------快读快输 ----------------------------------------
	
	//--------------------------------------------------------------------------------------------
	#define ll long long
	#define uint unsigned int
	#define ull unsigned long long
	#define INV(x) ksm(x,mod-2)
	const int maxn=100010,inf=1000000007;
	int mod=998244353;
	void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
	int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
	int fac[maxn],inv_fac[maxn],inv[maxn];
	void work_math()//阶乘
	{
		inv[1]=1;for(int i=2;i<=maxn-10;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
		fac[0]=inv_fac[0]=1;for(int i=1;i<=maxn-10;i++)fac[i]=1ll*fac[i-1]*i%mod;
		inv_fac[maxn-10]=INV(fac[maxn-10]);
		for(int i=maxn-11;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
	}
	struct disc{int x,y;};
	bool compp(disc x,disc y){return x.x<y.x;}
	void discretization(int *darr,int dn)//离散化 
	{
		static disc b[maxn];
		for(int i=1;i<=dn;i++)
		b[i].x=darr[i],b[i].y=i;
		sort(b+1,b+dn+1,compp);
		static int tot=0;b[0].x=-9666;
		for(int i=1;i<=dn;i++)
		if(b[i].x!=b[i-1].x)darr[b[i].y]=++tot;
		else darr[b[i].y]=tot;
	}
	template<class TY>TY gcd(TY x,TY y){return !y?x:gcd(y,x%y);}
	//----------------------------------------------板子---------------------------------------------
	
	//----------------------------------------------------------------------------------------------- 
	int T,n;char s[maxn];ll a[maxn],d[64];
	bool add(ll x)
	{
		for(int i=60;i>=0;i--)
		if(x&(1ll<<i))
		{
			if(d[i])x^=d[i];
			else {d[i]=x;return true;}
		}
		return false;
	}
	
	void main()
	{
		scanf("%d",&T);while(T--)
		{
			scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);scanf("%s",s+1);
			bool ans=true;
			for(int i=1;i<=n;i++)if(s[i]=='1')
			{
				memset(d,0,sizeof(d));
				for(int j=i+1;j<=n;j++)if(s[j]=='0')add(a[j]);
				if(add(a[i]))ans=false;
			}
			if(ans)printf("0n");else printf("1n");
		}
//		fcheck(1);return;
	}
}
 
int main(){my::main();return 0;}

T2

转化一下题意,就是要使序列的最大前缀和最小前缀和的差值最小。

不妨先将所有?定为-1,然后设 Z Z Z 为此时的最大前缀和,显然,此时的 Z Z Z 是我们能得到的最小的最大前缀和,然后设此时的最大的最小前缀和 f ( Z ) f(Z) f(Z),考虑怎么在不改变 Z Z Z 的前提下求出 f ( Z ) f(Z) f(Z)

这个东西可以考虑贪心,从左到右考虑?,原来在这里填的是-1,考虑能否变成1,即判断一下变成1之后是否有前缀和会超过 Z Z Z,假如没有,就贪心地使它变成1,显然这样不会变劣。

照这样看来,我们还需要求出所有 M ( M > Z ) M(M>Z) M(M>Z) f ( M ) f(M) f(M),然后用 M − f ( M ) M-f(M) Mf(M) 的最小值来作为答案。

但是实际上,可以发现这样一个性质: M − f ( M ) ≤ ( M + 2 ) − f ( M + 2 ) M-f(M)leq (M+2)-f(M+2) Mf(M)(M+2)f(M+2),因为当 M M M 加了 2 2 2 后,考虑求 f ( M + 2 ) f(M+2) f(M+2) 的过程,最多也就是多一个-1变成1而已,不可能让 ( M + 2 ) (M+2) (M+2) f ( M + 2 ) f(M+2) f(M+2) 的差值比 M M M f ( M ) f(M) f(M) 更小。

所以,我们其实只需要求出 Z Z Z Z + 1 Z+1 Z+1 f f f 就够了,因为从 Z + 2 Z+2 Z+2 开始他们的答案肯定不会比 Z Z Z Z + 1 Z+1 Z+1 的答案更优。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 1000010

int n,sum[maxn],msum[maxn];
char s[maxn];

int main()
{
	scanf("%s",s+1);n=strlen(s+1);
	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(s[i]=='1'?1:-1);
	msum[n]=sum[n];for(int i=n-1;i>=1;i--)msum[i]=max(msum[i+1],sum[i]);
	int mx=max(msum[1],0),mn,now,ans=n+1;
	for(int j=1;j<=2;j++)
	{
		now=mn=0;
		for(int i=1;i<=n;i++)
		{
			//判断这一位从-1变成1后,后面最大的前缀是否会超过mx
			if(s[i]=='?'){if(msum[i]-sum[i-1]+now+2<=mx)now++; else now--;}
			else if(s[i]=='1')now++; else now--;
			mn=min(mn,now);
		}
		ans=min(ans,mx-mn);mx++;
	}
	printf("%d",ans);
}

T3

这题难在转化题意,不能去考虑如何操作,要考虑如何判断一个序列是否能通过操作得到。

显然我们能得到全部都是 1 1 1 的序列,所以 A A A B B B 的值交换一下也无所谓的,不妨设 A ≤ B Aleq B AB

观察后可以发现,假如序列中存在长度大于等于 B B B 1 1 1 连续串,那么这个序列就一定可以操作出来,因为在这个 1 1 1 连续串之外的位置,是可以随意操作每一位为 0 0 0 1 1 1 的,比如说在这个连续串左边的一个位置 i i i,你可以通过将 [ i , i + B − 1 ] [i,i+B-1] [i,i+B1] 变为 1 1 1,然后再将 i i i 后面的 1 1 1 变成 0 0 0 即可。

进一步地,可以得到,假如将所有长度大于 A A A 的连续 0 0 0 段都变成 1 1 1,然后存在长度不小于 B B B 的连续 1 1 1 段,那么这个序列就是可以操作出来的。

然后考虑求出不能操作出来的序列数,然后用 2 n 2^n 2n 减去得到答案。这个序列肯定是由 0 0 0 段和 1 1 1 段交替组成的,其中 0 0 0 段长度不超过 A A A 1 1 1 段长度不超过 B B B,但是 1 1 1 段中可以包含长度不小于 A A A 0 0 0 段。

考虑 d p dp dp,设 g [ i ] [ 0 / 1 ] g[i][0/1] g[i][0/1] 表示长度为 i i i 的连续 1 1 1 段(可以包含长度不小于 A A A 0 0 0 段)并且以 0 / 1 0/1 0/1 结尾的方案数(但是开头一定是 1 1 1), f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1] 表示长度为 i i i 的以 0 / 1 0/1 0/1 结尾的不合法方案数,然后就可以 d p dp dp 了。

要特别注意的是,一个连续 1 1 1 段如果在序列的中间,那么它肯定头尾都是 1 1 1,但是如果在序列的最左边或最右边,那么是可以以 0 0 0 开头或以 0 0 0 结尾的。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mod 1000000007
#define maxn 5010
 
int n,a,b;
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int g[maxn][2],f[maxn][2];
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
 
int main()
{
	scanf("%d %d %d",&n,&a,&b);
	if(a>b)swap(a,b); g[1][1]=1;
	for(int i=2;i<b;i++)
	{
		g[i][1]=(g[i-1][0]+g[i-1][1])%mod;
		for(int j=a;j<i;j++)add(g[i][0],g[i-j][1]);
	}
	f[0][0]=f[0][1]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<min(a,i+1);j++)add(f[i][0],f[i-j][1]);
		for(int j=1;j<min(b,i+1);j++)
		{
			if(i==n)add(f[i][0],1ll*f[i-j][0]*g[j][0]%mod);
			if(i==j)add(f[i][1],(g[j][0]+g[j][1])%mod);
			else add(f[i][1],1ll*f[i-j][0]*g[j][1]%mod);
		}
	}
	printf("%d",(ksm(2,n)-(f[n][0]+f[n][1])%mod+mod)%mod);
}

最后

以上就是甜美月光最近收集整理的关于AGC045部分题解的全部内容,更多相关AGC045部分题解内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部