【学习笔记】AGC045

A - Xor Battle

算是比较难的 A A A题了,当时降智被卡了半天

显然倒着做,我们知道 A A A必胜的状态,记作集合 S S S。假设此时操作的是 A A A,那么 S ′ S' S的基等于 S S S的基和 a i a_i ai的并。假设此时操作的是 B B B,注意到 A A A必胜和 B B B必败是等价的,所以 S ′ = { x ∣ x ∈ S , x ⊕ a i ∈ S } S'=\{x|x\in S,x\oplus a_i\in S\} S={xxS,xaiS}。我们知道 S S S是一个线性空间,如果 a i ∈ S a_i\in S aiS那么 T ′ = S T'=S T=S,如果 a i ∉ S a_i\notin S ai/S,假设存在 x ∈ S , x ⊕ a i ∈ S x\in S,x\oplus a_i\in S xS,xaiS,那么根据线性空间的基本性质有 a i ∈ S a_i\in S aiS,显然矛盾,因此 T ′ = ∅ T'=\varnothing T=。线性基维护即可。

#include<bits/stdc++.h> #define ll long long #define pb push_back #define inf 0x3f3f3f3f using namespace std; ll f[65],a[205],m; int T,n; string s; void ins(ll x){ for(int i=62;i>=0;i--){ if(x>>i&1){ if(f[i])x^=f[i]; else{ f[i]=x;return; } } } } int qry(ll x){ for(int i=62;i>=0;i--){ if(x>>i&1){ if(f[i])x^=f[i]; else return 0; } }return 1; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; while(T--){ cin>>n,memset(f,0,sizeof f); for(int i=0;i<n;i++)cin>>a[i]; cin>>s;int ok=0; for(int i=n-1;i>=0;i--){ if(s[i]=='0')ins(a[i]); else if(!qry(a[i]))ok=1; } cout<<ok<<"\n"; } }

B - 01 Unbalanced

大致猜到了做法,但是不会证最后那个结论

问题相当于求 [ 0 : n ] [0:n] [0:n]前缀和的极差。考虑限制最大值的上界,在此前提下使得最小值最大。我们希望这个最大值的上界最小,因此二分即可。这个结论结合贪心的过程不难证明,当然如果你和我一样一开始没有把贪心的过程想清楚其实也可以猜到这个结论是正确的

所以我是不是降智了

似乎奇偶性这个地方有一点小细节,改一下就能过了

#include<bits/stdc++.h> using namespace std; const int N=1e6+5; string s; int n,Min,a[N],res2; stack<int>S; int check(int mid){ int m=0,m2=0;Min=0; while(S.size())S.pop(); for(int i=0;i<n;i++){ if(s[i]=='0')m--,a[i]=-1; else if(s[i]=='1'){ m++,a[i]=1; if(m>mid){ if(!m2)return 0; m-=2,m2--,a[S.top()]=-1,S.pop(); } } else{ if(m+1<=mid)m++,m2++,a[i]=1,S.push(i); else m--,a[i]=-1; }Min=min(Min,m); }return 1; } int main(){ cin>>s,n=s.size(); int l=0,r=n/2,res=0; while(l<=r){ int mid=l+r>>1; if(check(mid*2+1))res=mid,r=mid-1; else l=mid+1; } check(res*2+1); int m=0; for(int i=0;i<n;i++){ m+=a[i];assert(m<=res*2+1); Min=min(Min,m); }res2=res*2+1-Min; l=0,r=n/2+1,res=0; while(l<=r){ int mid=l+r>>1; if(check(mid*2))res=mid,r=mid-1; else l=mid+1; } check(res*2); m=Min=0; for(int i=0;i<n;i++){ m+=a[i];assert(m<=res*2); Min=min(Min,m); }res2=min(res2,res*2-Min); cout<<res2; }

C - Range Set

感觉这是一道非常套路的 d p dp dp

不得不说,套路有时候挺管用的,但是每道题都用同一种套路就有一点无聊了吧?

不妨设 A ≥ B A\ge B AB 挺迷的,第一步要限制一下大小,然后逆向操作,每次可以把连着的 A A A 0 0 0或者 B B B 1 1 1替换成任意字符,如果出现 A A A 0 0 0就肯定赢了 这个应该很显然吧 。那么就把 ≥ B \ge B B 1 1 1都替换成 0 0 0然后看有没有 ≥ A \ge A A 0 0 0即可。

这个 d p dp dp令人烦躁 假设我们处理了前 i i i个位置,然后要接一段 0 0 0上去,显然可以分成多个阶段去做,如果要接一段 1 1 1上去,但是这一段长度 < A <A <A,那么相当于区间加,如果这一段 ≥ A \ge A A,那么可以先一次性转移 A A A个字符,然后再分阶段转移。

复杂度 O ( n 2 ) O(n^2) O(n2)

其实有点像 D F A DFA DFA缩小状态数那种感觉,但是我太菜了代码比大佬的复杂了好几倍

最近老是手残,应该去治一治

#include<bits/stdc++.h> #define inf 0x3f3f3f3f #define ll long long #define pb push_back #define fi first #define se second using namespace std; const int mod=1e9+7; int n,A,B,f[5005][5005][3],f2[5005][5005][3]; //i am sb!!! //手残。。。 void add(int &x,int y){ x=(x+y)%mod; } signed main(){ cin>>n>>A>>B; if(A<B)swap(A,B); f[0][0][0]=f[0][0][1]=1; for(int i=0;i<n;i++){ for(int j=0;j<=A;j++){ add(f[i][j][0],f2[i][j][0]); add(f[i][j][1],f2[i][j][1]); add(f2[i+1][j][0],f2[i][j][0]); add(f2[i+1][j][1],f2[i][j][1]); if(f[i][j][0]){ add(f[i+1][min(A,j+1)][0],f[i][j][0]); add(f[i+1][min(A,j+1)][1],f[i][j][0]); } if(f[i][j][1]){ add(f2[i+1][(j>=A)?A:0][0],f[i][j][1]); add(f2[min(n+1,i+B)][(j>=A)?A:0][0],-f[i][j][1]); add(f[min(n+1,i+B)][min(A,j+B)][2],f[i][j][1]); add(f[min(n+1,i+B)][min(A,j+B)][0],f[i][j][1]); } if(f[i][j][2]){ add(f[i+1][min(A,j+1)][2],f[i][j][2]); add(f[i+1][min(A,j+1)][0],f[i][j][2]); } } } cout<<((ll)f[n][A][0]+f2[n][A][0]+mod+mod)%mod; }

D - Lamps and Buttons

困难的题目

首先最无脑的策略是,从亮着的灯中任意选一个来撸 因为排列 p p p是随机的,然后把这个环里面的灯全部撸完(也就是全部点亮),如果遇到自环就失败了。

但是这样似乎就和每个环里面亮着的灯的数目有关系,因此难以优化。

遗憾的是这道题比较数学所以不能找规律

那我们考虑一些比较脑洞的想法。没办法,这种题只能多尝试

首先我们考虑,直接从 1 1 1 A A A开始撸(这也是等价的最优策略),让我们先找到第一个 p i = i p_i=i pi=i的位置 t t t。然后对于 [ 1 : t − 1 ] [1:t-1] [1:t1]这些亮着的灯会把整个环撸完,对于 [ t + 1 : A ] [t+1:A] [t+1:A]的灯初始是亮着的因此最后也是亮着的,对于 [ A + 1 : n ] [A+1:n] [A+1:n]的点所在的环就必须至少有一个 [ 1 : t − 1 ] [1:t-1] [1:t1]中的点。

然后 [ 1 : t − 1 ] [1:t-1] [1:t1]不能有自环,这可以用容斥来解决。因此我们可以等价转化为:统计大小为 x + y + z x+y+z x+y+z的排列, ∀ i ∈ z \forall i\in z iz ∃ j ∈ x \exist j\in x jx i i i j j j在同一个环中。

接下来是一个固定的套路,但是我之前不太熟悉,不过它看起来非常正确:

将排列看成一个有向图,每次加入一个 i i i,有两种可能:新建一条边 ( i , i ) (i,i) (i,i)或者选择一条边 ( x , y ) (x,y) (x,y),删掉 ( x , y ) (x,y) (x,y)并加上 ( x , i ) , ( i , y ) (x,i),(i,y) (x,i),(i,y)

这个轮换令人想到斯特林数。。。。

不过这里应该有一步最基本的分析,那就是之前的枚举是 O ( n 2 ) O(n^2) O(n2),因此这里的计算应该是 O ( 1 ) O(1) O(1)的。事实上正解也非常巧妙:考虑在插入 x x x个数后立刻插入 z z z个数,这样 z z z只要不连自环,插在哪里都是合法的。因此方案数为 x ( x + y + z ) ! x + z \frac{x(x+y+z)!}{x+z} x+zx(x+y+z)!

回过头来反省一下,可能自己连那个转化都想不到吧。对最后那个计数的反应也慢了一点。

#include<bits/stdc++.h> #define inf 0x3f3f3f3f #define ll long long #define pb push_back #define fi first #define se second using namespace std; const int mod=1e9+7; const int N=1e7+5; int n,A; ll fac[N],inv[N],res; ll fpow(ll x,ll y=mod-2){ ll z(1); for(;y;y>>=1){ if(y&1)z=z*x%mod; x=x*x%mod; }return z; } void init(int n){ fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod; inv[n]=fpow(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod; } ll binom(ll x,ll y){ if(x<0||y<0||x<y)return 0; return fac[x]*inv[y]%mod*inv[x-y]%mod; } ll calc(ll x,ll y,ll z){ assert(x+z>=1); return x*fac[x+y+z]%mod*fac[x+z-1]%mod*inv[x+z]%mod; } int main(){ cin>>n>>A; init(n); for(int i=1;i<=A+1;i++){ for(int j=0;j<i;j++){ if(j&1){ res=(res-binom(i-1,j)*calc(i-1-j,max(0,A-i),n-A))%mod; } else{ res=(res+binom(i-1,j)*calc(i-1-j,max(0,A-i),n-A))%mod; } } }cout<<(res+mod)%mod; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530021.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示