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;
}
posted @ 2023-01-30 17:17  shrtcl  阅读(113)  评论(0编辑  收藏  举报