二次元音游人

这是一股照亮混沌的令和时代互联网的一道光,给在电子的海洋里冲浪的阿宅们带来笑容

Codeforces 2211 与集合相关的几个好题

いやに眩しいから
实在是耀眼得过头了啊

Nebius Round 2 (Codeforces Round 1088, Div. 1 + Div. 2)

A. Antimedian Deletion 429

image

image

显然是一直能删直到剩下两个,特判一下 \(n=1\) 即可

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[N];
inline void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    if(n==1){
        cout<<"1\n";
        return;
    }
    for(int i=1;i<=n;i++){
        cout<<2<<" ";
    }
    cout<<'\n';
    return;
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
} 

B. Mickey Mouse Constructive 981

image

把数组 \(a\) 分成若干个集合,然后要求每个集合的总和相同。有 \(x\)\(1\)\(y\)\(-1\)

不妨假设分成 \(k\) 个集合,那么每个集合总和得是 \(\frac{x-y}{k}\) ,为整数,所以最小的分段方法数量肯定是 \(x-y\) 的约数数量。比如说 \(x-y\)\(6\) ,把 \(a\) 数组分成两个集合的话,前缀和为 \(3\)\(6\) 的地方肯定能分割,同理分成 \(1,2,3,6\) 个集合也是可以的。

所以可以得到分段方法:

  • 如果 \(x>y\) ,那么 \(x-y>0\) ,先放 \(y\)\(-1\),再放 \(x\)\(1\),这样可以使得 \((0,x-y]\) 的数只出现一次,正好使得可能的分段次数等于约数数量

  • 如果 \(x<y\) ,那么 \(x-y<0\) ,先放 \(x\)\(1\),再放 \(y\)\(-1\),这样可以使得 \([x-y,0)\) 的数只出现一次,正好使得可能的分段次数等于约数数量

  • 如果 \(x=y\) ,那么先放 \(x\)\(1\) 或者先放 \(y\)\(-1\) 都可以 ,答案肯定是 \(1\),只能把所有元素放到一个集合

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=676767677;
int n;
const int N=2e5+5;
int x,y;
int sum[N<<1];
inline int qpow(int a,int b){
    int res=1;
    while(b){
        if(b&1){
            res=res*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return res%mod;
}
inline int div(int x){//计算x的约数个数
    if(x==0) return 1;
    int res=0;
    for(int i=1;i*i<=x;i++){
        if(x%i==0) {
            res++;
            if(i*i!=x) res++;
        }
    }
    return res;
}
inline void solve(){
    cin>>x>>y;
    int s=x-y;
    if(!s){
        cout<<"1\n";
        for(int i=1;i<=x;i++){
            cout<<"1 ";
        }
        for(int i=1;i<=x;i++){
            cout<<"-1 ";
        }
        cout<<'\n';
        return;
    }
    cout<<div(abs(s))<<'\n';
    if(x>y){
        for(int i=1;i<=y;i++){
            cout<<"-1 ";
        }
        for(int i=1;i<=x;i++){
            cout<<"1 ";
        }
        cout<<"\n";
        return;
    }
    else if(x<y){
        for(int i=1;i<=x;i++){
            cout<<"1 ";
        }
        for(int i=1;i<=y;i++){
            cout<<"-1 ";
        }
        cout<<"\n";
        return;

    }
    
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

C1. Equal Multisets (Easy Version) 1202

image

  • 设现在 \(a\) 数组统计到队列是 \([a_{i−k+1},a_{i−k+2},…,a_i]\), 遍历到下一个数时,出队 \(a_{i-k+1}\) ,入队 \(a_{i+1}\),且因为 \(a\) 数组是排列,所以 \(a_{i-k+1}\)\(a_{i+1}\) 不相等。那么要满足 \(b\) 的队列重排相等,需要满足 \(b_{i-k+1}=a_{i-k+1}\)\(b_{i+1}=a_{i+1}\)

  • 发现可能有一些元素是永远不出队也不入队的,范围位于 \([k,n-k+1]\),对于中间的这部分元素,直接使用桶来统计对应 \(a\)\(b\) 即可。注意 \(b\) 元素也同样需要是一个排列,简证:如果 \(b\) 数组缺了某元素,那么就不可能组成重排

  • 综上可以得到本题做法,对于两边的元素检查 \(a_i=b_i\),对于中间的元素检查可重集是否相同(忽略 \(b_i=-1\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
const int N=2e5+5;
int a[N],b[N],pos[N];
bool vis[N],vis2[N];
inline void solve(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        vis[i]=vis2[i]=false;
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
    }
    int L=n-k+1,R=k;
    if(L<=R){
        for(int i=L;i<=R;i++){
            vis[a[i]]=true;
        }
    }
    for(int i=1;i<=n;i++){
        bool flag=false;//是否在中间
        if(L<=i&&i<=R){
            flag=true;
        }
        else{
            flag=false;
        }
        if(flag){
            if(b[i]!=-1){
                if(!vis[b[i]]){
                    cout<<"NO\n";
                    return;
                }
                if(vis2[b[i]]){//b数组必须也是排列,所以不能有相同
                    cout<<"NO\n";
                    return;
                }
                vis2[b[i]]=true;
            }
        }
        else{//两边
            if(b[i]!=-1&&b[i]!=a[i]){
                cout<<"NO\n";
                return;
            }
        }
    }
    cout<<"YES\n";
    return;

}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
} 
/*
 1  2  4  3  5  6
-1 -1  3 -1 -1 -1

[pos-k+1,pos+k-1]

1
6 4
1 2 4 3 5 6
-1 -1 3 -1 -1 -1
*/

C2. Equal Multisets (Hard Version) 1736

image

\(a\) 出队 \(a_{i-k+1}\),入队 \(a_{i+1}\)\(b\) 出队 \(b_{i-k+1}\),入队 \(b_{i+1}\),想要 \(a,b\) 相等,有两种情况:

  • \(a_{i-k+1} \neq a_{i+1}\) 时,\(b_{i-k+1}=a_{i-k+1}\)\(b_{i+1}=a_{i+1}\)
  • \(a_{i-k+1} = a_{i+1}\) 时,\(b_{i-k+1}=b_{i+1}\)

第二条举例 \(k=2\) 来说 1 2 12 1 2,前两个集合相同,后两个集合也相同。

把数组 \(a\) 按模 \(k\) 分成 \(k\) 条链,每条链上的数有上面两条性质。

  • 如果一条链上的数都满足 \(a_{i-k+1}=a_{i+1}\) 的话,那么采取第二条性质,假设第一个数是 \(a_t(t<k)\),那么需要满足 \(a_{t+k}=a_t\)\(a_{t+2k}=a_t\)\(...\)\(a_{t+nk}=a_t\)

  • 如果一条链上的数有一个满足 \(a_{i-k+1}\neq a_{i+1}\) 的话,那么采取第一条性质,\(b_{i-k+1}=a_{i-k+1}\)\(b_{i+1}=a_{i+1}\),如果前面或者后面有 \(a_{j-k+1}=a_{j+1}\) ,那么因为第一条性质的影响,这整条链上的数都得满足第一条性质

\(a\) 数组的前 \(k\) 个元素拿出来记作 \(tar\) ,然后把 \(k\) 条链的满足元素记录下来,最后判断能否覆盖 \(tar\)

  • 如果一条链的 \(a_{p+nk}\) 都相等,那么对应 \(b\) 数组这条链上的数都得相等,找到链上第一个 \(b_i \neq -1\) 的数,继续遍历,如果后面又遇到一个不是 \(-1\) 的数,说明违法,输出 NO,否则就记录一下 \(b_i\) 来补充。特殊情况,\(b\) 数组的这条链上都是 \(-1\),那么说明我们有一个可以取任意数的灵活数来补充。

  • 如果一条链的 \(a_{t+nk}\) 不都相等,那么这条链上的数都得 \(a_i\) 等于 \(b_i\),最后记录一下 \(a_p\) 来补充

我们记录的补充的数其实是 \(b\) 数组的前 \(k\) 个元素是什么,最后需要确定 \(a\) 的前 \(k\) 个元素的可重集和 \(b\) 的前 \(k\) 个元素的可重集相等。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
const int N=2e5+5;
int a[N],b[N];
int tar[N];
inline void solve(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        tar[i]=0;
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
    }
    for(int i=1;i<=k;i++){
        tar[a[i]]++;
    }
    vector<int> V;
    int ano=0;
    for(int p=1;p<=k;p++){
        bool flag=true;
        for(int i=p+k;i<=n;i+=k){
            if(a[i]!=a[p]){
                flag=false;
                break;
            }
        }
        // cerr<<p<<" "<<flag<<endl;
        if(flag){
            int tmp=-1;
            for(int i=p;i<=n;i+=k){
                if(b[i]==-1) continue;
                if(tmp==-1){
                    tmp=b[i];
                    continue;
                }
                if(b[i]!=tmp){
                    cout<<"NO\n";
                    return;
                }
            }
            if(tmp!=-1){
                V.push_back(tmp);
            }
            else{
                ano++;
            }
        }
        else{
            for(int i=p;i<=n;i+=k){
                if(b[i]==-1) continue;
                if(a[i]!=b[i]){
                    cout<<"NO\n";
                    return;
                }
            }
            V.push_back(a[p]);
        }
    }
    for(int xx:V){
        tar[xx]--;
        if(tar[xx]<0){
            cout<<"NO\n";
            return;
        }
    }
    cout<<"YES\n";
    return;
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
} 

D. AND-array 1868

image

image

先考虑怎么用 \(a\) 表示 \(f(a)_k\),设 \(cnt_i\)表示 \(a\) 二进制第 \(i\) 位上有 \(cnt_i\)\(1\)

\(f(a)_k = \sum_{s=0}^{28} 2^s \cdot \binom{\text{cnt}_s}{k}\)

接下来考虑怎么倒着由 \(f(a)_k\) 推出 \(a\)

先看 \(b_n\),表示为 \(a_1 \& a_2 \& ...\&a_n\),那么如果第 \(k\) 位上是 \(1\) 的话就说明 \(a\) 的所有元素在这一位上都是 \(1\),那么 \(cnt_s=n\) 然后删掉这些数的贡献。对于 \(1\le i<n\),让 \(b_i=b_i- 2^s\times \binom{n}{i}\),这样之后就没有第 \(k\) 位上所有数都为 \(1\) 的贡献了。接下来让 \(n=n-1\) 循环执行以上操作即可求出原数组。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=2e5+5;
int fact[N];
inline int qpow(int a,int b){
    int res=1;
    while(b){
        if(b&1){
            res=res*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
inline int inv(int x){
    return qpow(x,mod-2)%mod;
}
inline int C(int n,int m){
    if(n<m) return 0;
    return fact[n]*inv(fact[m])%mod*inv(fact[n-m])%mod;
}
int n;
int b[N],a[N];
inline void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>b[i];
        a[i]=0;
    }
    vector<int> cnt(30,0);
    for(int i=n;i>=1;i--){
        for(int s=0;s<30;s++){
            if(b[i]>>s&1){
                cnt[s]=i;
                for(int j=1;j<=i;j++){
                    int tmp=C(i,j)*(1LL<<s)%mod;
                    b[j]=(b[j]-tmp+mod)%mod;
                }
            }
        }
    }
    for(int s=0;s<30;s++){
        for(int i=1;i<=cnt[s];i++){
            a[i]|=1<<s;
        }
    }
    for(int i=1;i<=n;i++){
        cout<<a[i]<<" ";
    }
    cout<<'\n';
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    fact[0]=1;
    for(int i=1;i<N;i++){
        fact[i]=fact[i-1]*i%mod;
    }
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

最后说一下 phigros这两年的愚人节铺面怎么越来越无聊了😰😭😭😭

posted @ 2026-03-31 00:51  超绝最可爱天使酱  阅读(43)  评论(0)    收藏  举报