2022牛客多校第二场CDE

C题
题意:nim游戏,先手赢的话,尽量赢的快,输的话尽量输的慢。
求最多的游戏局数,和先手执行的最优策略数
下面给两个结论:
1,石子数异或和为0的话,先手败,否则胜
2,先手败的话,可以构造出先后手后面都只取一个石子。
结论1证明略。
下证结论2
先手必败,也就是说xor_sum=0
先考虑构造游戏局数尽量多的方案,先手在lowbit最小的石子堆里取一个。
后手只能对称地在另一个lowbit相等的堆里取一个。比如先手取的是…100,取完后变成了…011,xor_sum->xor_sum^(111),后手必须构造出(111)抵消掉影响。如果后手选lowbit大于先手lowbit的堆,那么会对更高位造成影响。所以后手只能选lowbit的石子堆,取一个石子。也就是说这样后手是和先手对称着操作的,对称着去lowbit相等的石子堆。
上面构造出一种方案,使得总局数为sum,再考虑上面情况下先手第一步取的方案数
再理一下条件,现在先手只能取一个,要使得后手也只能取一个。我们考虑什么情况下,后手可以多取。设先手取的石子堆lowbit是第i位,若存在另一堆第i位为1且i不为lowbit,则后手可以多取。比如先手取10100,存在00110,先手取后xor_sum->xor_sum^(111),后手可以把两个1都取掉抵消掉(111)的影响。
其他情况:
1,第i位为0,那么怎么操作到无法抵消掉先手造成的对第i位的影响。
2,第i位为1且为lowbit,那后手只能对称取一个石子。。。
证毕(乱证)

下面考虑先手胜,那第一步要尽可能取多,且使得剩下异或和为0。怎么求最大取掉多少呢。
设原异或和为S,要从ai中选t个石子取掉,剩下异或和0.
先扣除掉ai,则异或和为S^ai,只要ai>(S ^ ai)则存在,t = ai - (S ^ ai),使得S ^ (ai -t)=0.
维护t和次数即可。
很nb的思考题!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int N=5e5+5;
const int mod=1e9+7;
const double eps=1e-8;
int n;
int a[N];
map<int,int> mp;
void solve(){
    cin>>n;
    int x_sum=0,sum=0;
    _for(i,1,n){
        cin>>a[i];
        x_sum ^= a[i];
        sum += a[i];
    }
    if( !x_sum ){
        int cnt=0;
        _for(i,1,n) mp[a[i]&-a[i]] = 0;
        _for(i,1,n){
            int t = a[i];
            t -= t&-t;
            while( t ){
                mp[t&-t]=1;
                t -= t&-t;
            }
        }
        _for(i,1,n) if( !mp[a[i]&-a[i]] ) cnt++;
        cout<<sum<<" "<<cnt<<endl;
    }
    else {
        int mx = 0,cnt=0;
        _for(i,1,n){
            if( a[i] > (a[i]^x_sum) ){
                int t = a[i] - (a[i]^x_sum);
                if( t > mx  ){
                    mx = t;
                    cnt=1;
                }
                else if( mx == t ) cnt++;
            }
        }
        cout<<sum-mx+1<<" "<<cnt<<endl;
    }
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    IOS;
    int T;cin>>T;
    while(T--) solve();
    AC; 
}

D题
题意:给n个物品间的兑换关系(n<=1e3),兑换关系如下a个x可以换wb个y,其中w是要求的,求最大的w,使得这些物品不能够无限制兑换。(无限制兑换指1个x换2个y,1个y能换10个x,这样可以无限兑换)
做法:考虑建图,二分w,然后check,边权为汇率,由于乘积很大,边权取个对,然后就是判有没有正环

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define double long double
const int N=1e3+5;
const int mod=1e9+7;
const double eps=1e-8;
int n,m;
int vis[N],cnt[N];
double dis[N];
std::vector< pair<int,double> > G[N];
queue<int> q;
bool ck(double mid,int st){//就是spaf求最短路
    mst(dis,0);
    mst(cnt,0);
    queue<int> q;
    _for(i,1,n){
        q.push(i);
        vis[i]=1;
    }
    while( !q.empty() ) {
        int x = q.front();q.pop();
        vis[x] = 0;
        for(auto [y,len] :G[x]){
            if( dis[x] + len + mid >  dis[y] ){
                dis[y] = dis[x] + len + mid;
                cnt[y]++;
                if( cnt[y] >= n ) return 0;
                if( !vis[y] ){
                    vis[y] = 1;
                    q.push(y);
                }
            }
        }
    }
    return 1;
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    // IOS;
    cin>>n>>m;
    _for(i,1,m){
        int b,d;
        double a,c;
        cin>>a>>b>>c>>d;
        G[b].push_back( {d,log2(c) - log2(a)});
    }
    double l = 0 , r = 1;
    _for(i,1,50){
        double mid = (l+r)/2;
        if( ck(log2(mid),1) ) l = mid;
        else r = mid;
    }
    printf("%.8Lf\n",r);
    AC; 
}


e题
题意:求长为n,含k个“bit”的字符串数,输出k从0到n的方案数,n<=1e6
做法:二项式反演+差卷积
考虑容斥,设 G k G_k Gk表示恰好有k个bit的方案数, F k F_k Fk为钦定含k个bit的方案数
F k = C n − 2 k k ∗ 2 6 n − 3 k F_k=C_{n-2k}^k*26^{n-3k} Fk=Cn2kk26n3k
G k = ∑ j = k n ( − 1 ) j − k ∗ C j k F j G_k=\sum_{j=k}^n(-1)^{j-k}*C_j^kF_j Gk=j=kn(1)jkCjkFj,组合数展开后分离系数
k ! ∗ G k = ∑ j = k n ( j ! F j ) ∗ ( − 1 ) j − k ( j − k ) ! k!*G_k = \sum_{j=k}^n(j!F_j)*\frac{(-1)^{j-k}}{(j-k)!} k!Gk=j=kn(j!Fj)(jk)!(1)jk
发现右边是差卷积形式,翻转其中一个多项式后求卷积即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int maxn = 6e6+10;
const int p = 998244353;
int Pow(int x,int d){
    int tans = 1;
    if(d == 0)return 1%p;
    int a = Pow(x,d/2);
    tans = 1ll*a*a%p;
    if(d%2)tans = 1ll*tans*x%p;
    return tans%p;
}
typedef vector<int> Poly;//多项式定义
int F1[maxn],F2[maxn];
int rev[maxn];
void NTT(int * A,int lim,int opt) {
    int i, j, k, m, gn, g, tmp;
    for(int i = 0; i < lim; ++i)rev[i] = (i & 1)*(lim >> 1) + (rev[i >> 1] >> 1);
    for(i = 0; i < lim; ++i)if (rev[i] < i) swap(A[i], A[rev[i]]);
    for(m = 2; m <= lim; m <<= 1) {
        k = m >> 1;
        gn = Pow(3,(p - 1) / m);
        for(i = 0; i < lim; i += m) {
            g = 1;
            for (j = 0; j < k; ++j, g = 1ll * g * gn % p) {
                tmp = 1ll * A[i + j + k] * g % p;
                A[i + j + k] = (A[i + j] - tmp + p) % p;
                A[i + j] = (A[i + j] + tmp) % p;
            }
        }
    }
    if(opt == -1){
        reverse(A+1,A+lim);
        int inv = Pow(lim,p-2);
        for(i = 0; i < lim; ++i) A[i] = 1ll * A[i] * inv % p;
    }
}
Poly mul(const Poly & A,const Poly & B){
    int n = A.size(),m = B.size();
    int siz = n + m - 1;
    Poly C(siz);
    if(siz < 64){//小于等于64项直接暴力算
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++)C[i+j] = (C[i+j] + 1LL*A[i]*B[j]%p)%p;
        }
        return C;
    }
    int fsiz = 1;
    while(fsiz <= siz)fsiz *= 2;
    for(int i = 0;i < fsiz;i++)F1[i] = F2[i] = 0;
    for(int i = 0;i < n;i++)F1[i] = A[i];
    for(int i = 0;i < m;i++)F2[i] = B[i];
    NTT(F1,fsiz,1);
    NTT(F2,fsiz,1);
    for(int i = 0;i < fsiz;i++)F1[i] = 1ll*F1[i]*F2[i]%p;
    NTT(F1,fsiz,-1);
    for(int i = 0;i < siz;i++){
        C[i] = F1[i];
    }
    return C;
}
const int N = 1e6+10;
ll qsm(int a,int b){
    int ans=1,tmp = a;
    while( b ){
        if( b&1 ) ans = (ans * tmp)%p;
        tmp = (tmp * tmp)%p;
        b>>=1;
    }
    return ans;
}
int fac[N],ni_f[N];
void ini(){
    fac[0]=1;
    int maxn = 1e6;
    _for(i,1,maxn) fac[i] =  (fac[i-1] * i)%p;
    ni_f[maxn] = qsm(fac[maxn],p-2);
    _rep(i,maxn-1,0) ni_f[i] = ni_f[i+1] * (i+1)%p;
}
ll C(int n,int m){
    if( m==n || m==0 ) return 1;
    if( n < m ) return 0;
    return fac[n] * ni_f[n-m]%p * ni_f[m]%p;
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    IOS;
    ini();
    int n;
    cin>>n;
    Poly f(n+1,0);
    _for(i,0,n){
        if( n-3*i < 0  ) break;
        f[i] = qsm(26,n-3*i)*C(n-2*i,i)%p*fac[i]%p;
    }
    Poly g(n+1,0);
    _for(i,0,n){
        int flag = ((n-i)&1)?-1:1;
        g[i] = (p + flag*ni_f[n-i])%p;
    }
    Poly h = mul(f,g);
    _for(i,n,2*n) cout<<h[i]*ni_f[i-n]%p<<" ";
    AC;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值