标签:printf namespace 转化 linear oid 做了 clear math flag
这场比赛真的是鸽了太久了的说,一来题挺难的,二来中间因为ZJOI和市统测占用了不少时间
所以题目都记不太得了随便胡一下吧
PS:太菜了所以只做了ABCD,其中BCD都是看一半题解才会的菜哭
首先肯定考虑倒着做,我们维护\(0\)玩家能获胜的集合\(S\),考虑从后往前判断:
为什么在\(1\)玩家的回合我们可以这么做?因为考虑\(0\)玩家的回合时我们相当于维护了一个线性基
若\(a_i\in 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;
}
首先考虑转化问题,把\(0\)看作\(-1\),\(1\)看作\(1\),然后一个区间的贡献就是区间和的绝对值
转化一下我们发现只要求出前缀和\(pre_i\),\(pre\)的极差就是最大贡献,所以我们要最小化极差
然后很naive想着随便枚举一下久好了,但是一个顺序问题怎么都搞不来,直接GG
看了题解,由于这里有两个border,所以我们卡死一维再优化另一个
考虑枚举\(pre\)的\(\min\)为\(l\),那么现在我们要保证\(pre_i\ge l\)的情况下最小化最大值
首先我们考虑先把?
全填成\(1\),然后贪心地从前往后改,考虑是否把\(1\)改成\(0\)(因为改后面的一定不会比前面优),并保证\(pre_i\ge 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<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;
}
标签:printf namespace 转化 linear oid 做了 clear math flag
原文地址:https://www.cnblogs.com/cjjsb/p/13254162.html