2022牛客多校第二场CDE

Posted 吃花椒的妙酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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=Cn2kk(26n3k)
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=(以上是关于2022牛客多校第二场CDE的主要内容,如果未能解决你的问题,请参考以下文章

2022牛客多校第一场ACDGIJ

2022牛客多校第一场ACDGIJ

2022牛客多校第一场ACDGIJ

2022牛客多校第四场C.Easy Counting Problem(EGF+NTT)

2022牛客多校第四场C.Easy Counting Problem(EGF+NTT)

2019牛客多校第一场