牛客练习赛76
B zzugzx (vs) Kurisu
是一个博弈游戏
注意到(m+1)^n<=5000
那么我们是可以直接考虑爆搜的
总共N个回合,那么两个人就是2*N次操作
定义f[a][b]代表当 ,zzugzx 选了a的数,Kurisu选了b的数,zzugzx赢的概率
a和b分别是n位m+1进制的数,代表n回合他抽到1-m的数放在1~n哪个位置
#include<bits/stdc++.h>
using namespace std;
int ok[5000][5000];
double f[5000][5000];
int n,m;
double dfs(int t,int a,int b)
{
if(t==0)
return a>b;
if(ok[a][b])return f[a][b];
ok[a][b]=1;
if(t%2==0)
{
for(int i=1;i<=m;i++)
{
int now=a;
int base=i;
double maxx=0;
for(int j=1;j<=n;j++)
{
if(now%(m+1)==0)maxx=max(maxx,dfs(t-1,a+base,b));//zzugzx要使自己赢得概率大
base*=m+1;
now/=m+1;
}
f[a][b]+=maxx/m*1.0;//从最大处转移
}
}
else
{
for(int i=1;i<=m;i++)
{
int now=b;
int base=i;
double minn=1;
for(int j=1;j<=n;j++)
{
if(now%(m+1)==0)minn=min(minn,dfs(t-1,a,b+base));//Kurisu要使对手输的概率大
base*=m+1;
now/=m+1;
}
f[a][b]+=minn/m*1.0;//从最小处转移
}
}
return f[a][b];
}
int main()
{
cin>>n>>m;
printf("%.8lf\n",dfs(2*n,0,0));
}
E 牛牛数数
思路:
先求出线性基
通过贪心或者二分求出有多少个值通过线性基计算小于等于K
通过所有情况减去小于等于的情况,即为答案
注意:需要判断0的情况,线性基无法特判0
贪心:`
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
ll f=0;
ll p[100];
ll cnt[100];
ll tot;
void add(ll x)
{
for(ll i=62;i>=0;i--)
{
if(x&(1ll<<i))
{
if(p[i]==0)
{
p[i]=x;
return ;
}
else
{
x^=p[i];
}
}
}
f=1;
}
void rb()
{
for(ll i=62;i>=1;i--)
{
for(ll j=i-1;j>=0;j--)
{
if(p[i]&(1ll<<j))
{
p[i]^=p[j];
}
}
}
for(ll i=0;i<=62;i++)
if(p[i])
cnt[tot++]=p[i];
}
ll ask(ll k)
{
ll ans=0;
ll ct=0;
for(ll i=tot-1;i>=0;i--)
{
if((ans^cnt[i])<=k)
{
ans^=cnt[i];
ct+=(1ll<<i);
}
}
if(f)
ct++;
return ct;
}
int main()
{
cin>>n>>k;
for(ll i=1;i<=n;i++)
{
ll a;
cin>>a;
add(a);
}
rb();
cout<<(1ll<<tot)+f-1ll-ask(k)<<endl;
return 0;
}
二分
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,tot = 0;
long long u;
long long a[1000005];
long long k[61],tt,x,cnt;
bool flag = 0;
void insert(long long x){
for (ll i=60;i>=0;i--){
if (x&((long long)1LL<<i)) {
if (k[i]) x=x^k[i];
else {
tot++;
k[i]=x;
return ;
}
}
}
flag=1;
}
long long ask(long long x) {
if (tot < n && x == 1) return 0;
if (tot < n) x--;
if (x >= (1LL << (long long)cnt)) return -1;
long long ans = 0;
for (ll i = 0;i <= 60;i++) {
if (k[i]) {
if (x % 2) ans ^= (long long)k[i];
x /= 2;
}
}
return ans;
}
int main(){
cin>>n>>u;
for (ll i=1;i<=n;i++) {
cin>>a[i];
insert(a[i]);
}
for (ll i = 0;i <= 60;i++) {
for (ll j = 1;j <= i;j++) {
if (k[i] & ((long long)1LL << (long long)(j - 1))) k[i] ^= (long long)k[j - 1];
}
if (k[i]) cnt++;
}
ll l=1,r = (1ll <<(tot))-1+flag;
ll sum=1;
while(l<=r)
{
ll mid=(l+r)>>1;
if(ask(mid)>u)
{
r=mid-1;
sum=mid;
}
else
{
l=mid+1;
}
}
ll ans=1;
cout<<(1ll<<tot)+flag-sum<<endl;
return 0;
}