zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 045

    Preface

    这场比赛真的是鸽了太久了的说,一来题挺难的,二来中间因为ZJOI和市统测占用了不少时间

    所以题目都记不太得了随便胡一下吧

    PS:太菜了所以只做了ABCD,其中BCD都是看一半题解才会的菜哭


    A - Xor Battle

    首先肯定考虑倒着做,我们维护(0)玩家能获胜的集合(S),考虑从后往前判断:

    • 当前为(0)玩家的回合,那么(S'=Scup{xoperatorname{xor} a_i,xin S})
    • 当前为(1)玩家的回合,那么若(a_iin S),则(S'=S);否则(S'=emptyset)

    为什么在(1)玩家的回合我们可以这么做?因为考虑(0)玩家的回合时我们相当于维护了一个线性基

    (a_iin S)那么无论哪个数(operatorname{xor} a_i)都始终在线性基内,那么必输

    否则它随便异或上一个数都不在(S)中(考虑异或的性质),那么必胜

    所以直接线性基维护即可

    #include<cstdio>
    #define RI register int
    using namespace std;
    typedef long long LL;
    const int N=205;
    struct Linear_Basis
    {
    	LL p[63];
    	inline void clear(void)
    	{
    		for (RI i=0;i<63;++i) p[i]=0;
    	}
    	inline void insert(LL x)
    	{
    		for (RI i=62;~i;--i) if ((x>>i)&1)
    		{
    			if (!p[i]) return (void)(p[i]=x); x^=p[i];
    		}
    	}
    	inline bool exist(LL x)
    	{
    		for (RI i=62;~i;--i) if ((x^p[i])<x) x^=p[i]; return !x;
    	}
    }l;
    int t,n; LL a[N]; char s[N];
    int main()
    {
    	for (scanf("%d",&t);t;--t)
    	{
    		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%lld",&a[i]);
    		scanf("%s",s+1); if (s[n]=='1') { puts("1"); continue; }
    		bool flag=0; for (l.clear(),i=n;i;--i) if (s[i]=='1')
    		{
    			if (!l.exist(a[i])) { flag=1; break; }
    		} else l.insert(a[i]);
    		puts(flag?"1":"0");
    	}
    	return 0;
    }
    
    

    B - 01 Unbalanced

    首先考虑转化问题,把(0)看作(-1)(1)看作(1),然后一个区间的贡献就是区间和的绝对值

    转化一下我们发现只要求出前缀和(pre_i)(pre)的极差就是最大贡献,所以我们要最小化极差

    然后很naive想着随便枚举一下久好了,但是一个顺序问题怎么都搞不来,直接GG

    看了题解,由于这里有两个border,所以我们卡死一个再优化另一个

    考虑枚举(pre)(min)(l),那么现在我们要保证(pre_ige l)的情况下最小化最大值

    首先我们考虑先把?全填成(1),然后贪心地从前往后改,考虑是否把(1)改成(0)(因为改后面的一定不会比前面优),并保证(pre_ige l)

    然后就有了一个(O(n^2))的做法,设(min{pre_i}=M),枚举(l=M,M-1,cdots 0)

    观察贪心的过程,容易发现当(l=M-2)时的答案一定不会优于(l=M)时的答案,(l=M-3)时同理

    因此只用考虑(l=M)(l=M-1)的情况即可

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=1000005;
    char s[N]; int n,pfx[N],smi[N];
    inline int work(CI mi)
    {
    	RI i; int cur=0,mx=0; for (i=1;i<=n;++i)
    	{
    		if (s[i]=='?'&&smi[i]-cur-2>=mi) cur+=2;
    		mx=max(mx,pfx[i]-cur);
    	}
    	return mx-mi;
    }
    int main()
    {
    	RI i; for (scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i)
    	pfx[i]=pfx[i-1]+(s[i]=='0'?-1:1); smi[n]=pfx[n];
    	for (i=n-1;~i;--i) smi[i]=min(smi[i+1],pfx[i]);
    	return printf("%d",min(work(smi[0]),work(smi[0]-1))),0;
    }
    
    

    C - Range Set

    首先我们发现(A,B)可以互换,因为可以在开始就把整个序列染成(1)然后将两种操作反转过来

    因此我们考虑对于最后结果的序列(S),任意一个长度大于等于(A)的连续(0)区间可以把它染成任意一种颜色

    同理对于任意一个长度大于等于(B)的连续(1)区间可以把它染成任意一种颜色

    因此我们得到了判断一个序列是否合法的方法:将序列中所有长度大于等于(A)的连续(0)区间染成(1),若存在长度大于等于(B)(1)区间,则序列合法

    那么首先我们可以先DP出包含大于等于(A)个连续的(0)的长度为(i)的区间方案数(g_i),然后DP出不包含大于等于(A)个连续的(0)且不包含大于等于(B)个连续的(1)的串的方案数,用(g_i)做转移系数即可(说的好绕但很好写的)

    #include<cstdio>
    #include<iostream> 
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=5005,mod=1e9+7;
    int n,a,b,f[N][2],g[N],dp[N][2],ans;
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    inline int sum(CI x,CI y)
    {
    	int t=x+y; return t>=mod?t-mod:t;
    }
    inline int sub(CI x,CI y)
    {
    	int t=x-y; return t<0?t+mod:t;
    }
    inline int quick_pow(int x,int p,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    int main()
    {
    	RI i,j; scanf("%d%d%d",&n,&a,&b); if (a>b) swap(a,b);
    	if (b==1) return printf("%d",quick_pow(2,n)),0;
    	for (f[0][0]=f[0][1]=i=1;i<=n;++i) for (j=1;j<=i;++j)
    	{
    		if (j>=a) inc(f[i][0],f[i-j][1]); inc(f[i][1],f[i-j][0]);
    	}
    	for (g[0]=i=1;i<=n;++i) g[i]=sum(f[i][0],f[i][1]);
    	for (i=1;i<n;++i) 
    	{
    		if (i<a) dp[i][0]=1; for (j=1;j<min(i,a);++j) inc(dp[i][0],dp[i-j][1]);
    		if (i<b) dp[i][1]=g[i-1]; for (j=1;j<min(i,b);++j)
    		inc(dp[i][1],1LL*dp[i-j][0]*(j<=2?1:g[j-2])%mod);
    		if (n-i<a) inc(ans,dp[i][1]); if (n-i<b) inc(ans,1LL*dp[i][0]*g[n-i-1]%mod);
    	}
    	return printf("%d",sub(quick_pow(2,n),ans)),0;
    }
    
    

    D - Lamps and Buttons

    刚开始读错题自闭了好久的说,后来发现原来Snuke不知道(p_i),那么其实排列随机,因此Snuke的策略其实没有影响

    因此我们直接假定Snuke每次从编号小到大依次尝试按下一盏亮着的且未被操作的灯,容易发现有以下情况:

    • (p_i=i),此时(i)熄灭,Snuke失败
    • (p_i ot =i),若(p_i)暗了则重新点亮它。容易发现此时Snuke可以转而操作(p_i),直到把整个置换环上所有灯都点亮

    因此我们可以得出一个合法的排列要满足:

    • (t)为最小的(p_t=t)的位置,若不存在则(t=A+1)
    • 对于所有的(A+1le ile N)(i)所在的置换环上至少存在一个(le t-1)的元素

    考虑枚举(t)的位置,显然这样会有重复,所以我们还要枚举前面有多少个自环来容斥

    考虑现在其实整个序列中的数有三类,一类是([1,t-1])的(设有(a)个),一类是([A+1,N])的(设有(b)个),一类是([t+1,A])的(设有(c)个)

    考虑如何求方案数(f(a,b,c)),首先是前(a)个数有(a!)种放法,然后考虑加入(b)个元素,因为每个元素都要插入前面的一个环内,因此方案数是(a^{overline{b}})

    对于最后(c)个元素,因为可以随便放,因此方案数是((a+b+1)^{overline c}),因此

    [f(a,b,c)=a! imes frac{(a+b-1)!}{(a-1)!} imes frac{(a+b+c)!}{(a+b)!}\ =(a+b+c)! imes frac{a}{a+b} ]

    所以就做完了,复杂度(O(A^2+N))

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=1e7+5,mod=1e9+7;
    int n,m,fact[N],inv[N],ans;
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    inline void dec(int& x,CI y)
    {
    	if ((x-=y)<0) x+=mod;
    }
    inline int quick_pow(int x,int p=mod-2,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    inline void init(CI n)
    {
    	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
    	for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
    }
    inline int C(CI n,CI m)
    {
    	return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
    }
    inline int calc(CI a,CI b,CI c)
    {
    	return 1LL*fact[a+b+c]*a%mod*quick_pow(a+b)%mod;
    }
    int main()
    {
    	RI i,j; for (scanf("%d%d",&n,&m),init(n),i=1;i<=m+1;++i)
    	for (j=0;j<i;++j) if (j&1) dec(ans,1LL*calc(i-1-j,n-m,max(m-i,0))*C(i-1,j)%mod);
    	else inc(ans,1LL*calc(i-1-j,n-m,max(m-i,0))*C(i-1,j)%mod);
    	return printf("%d",ans),0;
    }
    
    

    Postscript

    最近做题好慢啊,不过暑假要来了,能不能最后冲一次呢?

  • 相关阅读:
    [好好学习]在VMware中安装Oracle Enterprise Linux (v5.7)
    [冲昏头脑]IDEA中的maven项目中学习log4j的日志操作
    [烧脑时刻]EL表达式1分钟完事
    Sublime2 破解教程
    全脑瘫IT时代(八)
    全脑瘫IT时代(九)
    迁移完成
    USB Debug Cable (一)
    一个不是很常见的.Net Interop问题
    全脑瘫IT时代(十二)
  • 原文地址:https://www.cnblogs.com/cjjsb/p/13254162.html
Copyright © 2011-2022 走看看