Codeforces 2211 与集合相关的几个好题
いやに眩しいから
实在是耀眼得过头了啊
Nebius Round 2 (Codeforces Round 1088, Div. 1 + Div. 2)
A. Antimedian Deletion 429


显然是一直能删直到剩下两个,特判一下 \(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

把数组 \(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

-
设现在 \(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

\(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 1 和 2 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


先考虑怎么用 \(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这两年的愚人节铺面怎么越来越无聊了😰😭😭😭

浙公网安备 33010602011771号