gym103469 XXII Open Cup, Grand Prix of IMO
A. AND
找到最小的值 \(a\),如果存在 \(x\and a\not=a\) 无解。
否则可以把 \(a\) 作为 \(0\) 使用,即在每两个数之间放上 \(a\)。
#include <bits/stdc++.h>
using namespace std;
void solve(){
int n;
scanf("%d",&n);
vector<int> a(n);
for(int&x:a) scanf("%d",&x);
int mi=*min_element(a.begin(),a.end());
for(int x:a) if((x&mi)!=mi) return(void) puts("-1");
printf("%d\n",n*2);
for(int x:a) printf("%d %d ",x,mi);
puts("");
}
int main(){
int t;
scanf("%d",&t);
while(t--) solve();
return 0;
}
E. Eulerian?
考虑一个必要条件:将图分成两部分,这两部分之间边的数量必须是偶数。
随机足够多次可以保证是充分的?不会证。
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
mt19937 rnd(time(0));
int n;cin>>n;
vector<int> p(n);
iota(p.begin(),p.end(),0);
auto query=[&](auto&v){
cout<<"? "<<v.size();
for(int x:v) cout<<" "<<x+1;
cout<<endl;
int res;cin>>res;
return res;
};
auto answer=[&](int x){
cout<<"! "<<(x?"YES":"NO")<<endl;
exit(0);
};
int m=query(p);
for(int t=29;t--;){
vector<int> p0,p1;
for(int x:p)
if(rnd()%2) p0.push_back(x);
else p1.push_back(x);
if((m-query(p0)-query(p1))%2) answer(0);
}
answer(1);
}
H. Hamiltonian
- 大小为 \(n\) 的环:\(n\) 对。
- \(K_n\):\(\binom{n}{2}\) 对。
考虑在环上挂一个完全图。
令极大的完全图大小为 \(a\),环上剩下 \(b\) 个点 \((a\ge3,b\ge2)\),则共有 \((\binom{a}{2}-1)+(b-1)+2(a-1)\) 对。
发现此时能覆盖 \([1,60]\)。
#include <bits/stdc++.h>
using namespace std;
int main(){
int k;
scanf("%d",&k);
if(k==1) return puts("2 1\n1 2"),0;
if(k==2) return puts("4 4\n1 2\n1 3\n2 3\n3 4"),0;
if(k<=20){
printf("%d %d\n",k,k);
for(int i=1;i<=k;i++) printf("%d %d\n",i,i%k+1);
return 0;
}
for(int a=3;a<20;a++) for(int b=2;a+b<=20;b++) if(a*(a+3)/2+b-4==k){
printf("%d %d\n1 %d\n",a+b,a*(a-1)/2+b+1,a+b);
for(int i=1;i<a;i++) for(int j=i+1;j<=a;j++) printf("%d %d\n",i,j);
for(int i=a;i<a+b;i++) printf("%d %d\n",i,i+1);
return 0;
}
}
J. Joke
先把 \(p\) 排成 \((1,2,\cdots,n)\) 的形式。
考虑若 \(i<j\and q_i>q_j\),则 \(s_i=1\Rightarrow s_j=0\)。
一个 \(q\) 中的上升序列可以对应 \(s_i=1\) 的位置,构造是容易的。
至此只需要对可能的上升序列计数,设 \(f(i,j,k)\) 为前 \(i\) 个数用了 \(j\) 个未被钦定的位置上一个大小为 \(k\) 的方案数。
转移很简单。答案是 \(\sum f(n,i,j)(n-i-\sum[q_i\not=0])!\)。
时间复杂度:\(O(n^4)\)。
#include <bits/stdc++.h>
using namespace std;
const int N=105,mod=998244353;
int n,m,ans,p[N],q[N],b[N],h[N],f[N][N][N];
inline void fix(int&x) {if(x>=mod) x-=mod;}
int main(){
scanf("%d",&n);h[0]=1;
for(int i=1;i<=n;i++) scanf("%d",&p[i]),h[i]=1ll*i*h[i-1]%mod;
for(int i=1,x;i<=n;i++) scanf("%d",&x),b[q[p[i]]=x]=1,m+=x>0;
f[0][0][0]=1;
for(int i=1;i<=n;i++) for(int j=0;j<i;j++) for(int k=0;k<=n;k++){
int v=f[i-1][j][k];
if(!v) continue;
fix(f[i][j][k]+=v);
if(q[i]) fix(f[i][j][q[i]]+=(q[i]>k)*v);
else for(int t=k+1;t<=n;t++) if(!b[t]) fix(f[i][j+1][t]+=v);
}
for(int i=0;i<=n-m;i++) for(int j=0;j<=n;j++) fix(ans+=1ll*f[n][i][j]*h[n-i-m]%mod);
printf("%d\n",ans);
return 0;
}
K. K-onstruction
首先很难想出一个正经构造,只能考虑乱搞。
假设现在集合 \(S\) 只有正整数,那么加入负数的贡献是 \(S\) 中凑成其绝对值的方案数。
发现怎么加都是没有影响的,所以考虑先生成出一个正整数集合,使背包方案数后本质不同的数尽量多,之后用这些方案数再做一个背包,凑出所有 \([1,10^6]\) 的方案。
多试几道,随机生成一个大小为 \(22\),值域为 \([1,5]\) 的正整数集合,剩下的 \(8\) 次刚好能卡过去。
#include <bits/stdc++.h>
using namespace std;
const int K=22,N=1e6;
vector<int> a{3,4,5,4,2,4,4,1,4,3,4,3,3,5,3,2,5,1,1,4,3,3},g(K*5+1),f(N+1,N),l(N+1);
void solve(){
int n;
scanf("%d",&n);
printf("%d\n",f[n]+K);
for(int x:a) printf("%d ",x);
for(;n>1;n-=g[l[n]]) printf("%d ",-l[n]);
puts("");
}
int main(){
int s=accumulate(a.begin(),a.end(),0);
for(int i=0;i<1<<K;i++){
int sum=0;
for(int j=0;j<K;j++) if(i>>j&1) sum+=a[j];
g[sum]++;
}
f[1]=0;
for(int i=1;i<=N;i++) for(int j=s/2+1;j<=s;j++)
if(i+g[j]<=N&&f[i]+1<f[i+g[j]]) f[i+g[j]]=f[i]+1,l[i+g[j]]=j;
int t;
scanf("%d",&t);
while(t--) solve();
return 0;
}