第十届“图灵杯”NEUQ-ACM程序设计竞赛个人赛 题解

A .有用的算法

题意:

给你一个数组,要你判断是否是单调不降,或者单调不升的

做法:

直接模拟判断即可

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 



void solve(){
	
	int n;
	cin>>n;
	vector<int>a(n),s1(n),s2(n);
	for(int i=0;i<n;i++){
		cin>>a[i];
		s1[i]=s2[i]=a[i];
	}
	sort(s1.begin(),s1.end());
	sort(s2.begin(),s2.end(),greater<int>());
	if(a==s1||a==s2){
		cout<<"erfen is useful!\n";
	}else{
		cout<<"bukeyi\n";
	}

}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

B 平衡数

题意:

给你一个四位数,问你前两位的数位和与后两位的数位和相不相等

做法:

模拟判断

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 



void solve(){
	string s;
	cin>>s;
	int a=(s[0]-'0')+(s[1]-'0');
	int b=(s[2]-'0')+(s[3]-'0');
	cout<<(a==b?"YES\n":"NO\n");
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    //t=1;
    cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

C 三角形

题意:

给你三个点:\(A(0.0),B(x_b,0),C(x_c,y_c)\) 构成的三角形

问点\(P(x_p,y_p)\) 是否在三角形内部(当落在三角形边上的时候不算内部)

做法:

分情况讨论:

  • \(x_p>=x_b||y_p>=y_c\) ,这种情况\(P\)点一定落在\(B\) 点右侧或者与其重合,或者落在\(C\)点上侧或与其重合,这种情况一定是在外部

  • 否则我们讨论:\(x_p<x_b\ \&\&\ y_p<y_c\) 的情况

    \(x_c=x_p\) 一定是在三角形内部

    然后我们发现我们就可以把三角形分成两块区域:\(0<x< x_c\)\(x_c<x<x_b\)

    我们可以利用相似三角形成对应边比例判断点 \(P\) 是否落在直线\(AC\) 或者直线\(BC\) 的下方即可

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 



void solve(){
	
	int xb,xc,yc;
	cin>>xb>>xc>>yc;
	int xp,yp;
	cin>>xp>>yp;
	if(xp<xb&&yp<yc){
		if(xp==xc)cout<<"yes\n";
		else if(xp<xc){
			if(yp*xc<xp*yc)cout<<"yes\n";
			else cout<<"no\n";
		}else{
			if(yp*(xb-xc)<yc*(xb-xp))cout<<"yes\n";
			else cout<<"no\n";
		}
	}else{
		cout<<"no\n";
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

D 文稿修订

题意:
给你一堆字符串,要你把其中的 \(NEUQ\) 全改成 \(WOW\ NEUQ\)

同时你需要统计字符串中所有非全是大写的 \(NEUQ\) 的数量

做法:

字符串模拟,\(stringstream\) 的应用

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;


void solve(){

	vector<string>ans;
	string s;
	int cnt=0;
	while(getline(cin,s),s!="#"){
		stringstream ssin(s);
		string ss,res;
		while(ssin>>ss){
			if(ss=="NEUQ"){
				if(res.size())res+=" ";
				res+="WOW NEUQ";
			}else{
				if(res.size())res+=" ";
				res+=ss;
				if(ss.size()==4){
					bool ok=false;
					for(auto &c:ss){
						if(c>='a'&&c<='z')ok=true;
						if(c>='A'&&c<='Z')c+=32;
					}
					if(ss=="neuq"&&ok)cnt++;
				}
			}
		}
		ans.push_back(res);
	}
	cout<<cnt<<'\n';
	for(auto s:ans){
		cout<<s<<'\n';
	}
}


int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

E 减肥计划

题意:

给你 \(n\) \((1\leq n\leq10)\)种食物,有 \(x,y,z,w\) 四种属性,每种食物都有吃或者不吃两种选择,现在要求你所吃食物的 \(\sum x<=a,\sum y<=b,\sum z<=c\)

问你 \(\sum w\) 最大是多少

做法:

发现 \(n\) 很小,直接用二进制枚举或者 \(dfs\) 枚举所有可能,取最大值即可

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 

struct node{
	int x,y,z,w;
}bag[20];

void solve(){
	int n,a,b,c;
	cin>>n>>a>>b>>c;
	for(int i=0;i<n;i++){
		auto &[x,y,z,w]=bag[i];
		cin>>x>>y>>z>>w;
	}
	int ans=0;
	for(int i=0;i<1<<n;i++){
		int sa=0,sb=0,sc=0,res=0;
		for(int j=0;j<n;j++){
			if(i>>j&1){
				auto [x,y,z,w]=bag[j];
				sa+=x,sb+=y,sc+=z,res+=w;
			}
		}
		if(sa<=a&&sb<=b&&sc<=c){
			ans=max(ans,res);
		}
	}
	cout<<ans<<endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

F 吃包子

题意:

你有 \(n\) 个包子,\(a_i=0\) 表示素包子,\(a_i=1\) 表示肉包子,现在你需要选择连续的一段把他吃掉,要求你所吃的素包子数量 \(\leq m\)

问你在这种情况下,最多能吃到多少肉包子

做法:

双指针经典题,定义 \(L\) ,为所有满足 \([L',R]\) 中素包子数量 \(\leq m\)\(L'\) 中最小的那个 ,我们发现当 \(R\) 往右移动的时候,\(L\) 也是单调往右走

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 

const int N=1e6+10;
int a[N];

void solve(){
	
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	//two point
	int ans=0;
	for(int i=1,j=1,sum=0;i<=n;i++){
		sum+=a[i]==0;
		while(sum>m){
			sum-=a[j++]==0;
		}
		ans=max(ans,i-j+1-sum);
	}
	cout<<ans<<'\n';
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

G 数字鉴定

题意:

给你 \(n\) 个区间,然后 \(q\) 次询问,每次给你一个数 \(x\) ,问你在 \(n\) 个区间的并集中,是否不存在任何 \(x\) 的倍数

做法:

观察区间范围 \(1\leq l\leq r\leq10^6\), 故我们可以使用差分来进行区间求并集

然后差分做完求一遍前缀和得到数组 \(b\) ,若 \(b[i]\) 不为 \(0\),表示这个数出现过在区间的并集中

然后我们只要利用 \((nlogn)\) 复杂度求出所有\(1\leq x\leq10^6\) 数的倍数 \(y\),当我们发现 \(b[y]\) 出现过,就给 \(x\) 打上标记表示在区间并中存在他的倍数

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 
typedef pair<int,int>PII;
const int N=1e6+10;
int b[N]; //差分
bool ok[N];

void solve(){
	
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++){
        int l,r;
		cin>>l>>r;
        b[l]++,b[r+1]--;
	}

	for(int i=1;i<N;i++){
		b[i]+=b[i-1];
	}
	for(int i=1;i<N;i++){
		for(int j=i;j<N;j+=i){
			if(b[j])ok[i]=true;
		}
	}
	while(q--){
		int x;
		cin>>x;
		cout<<(!ok[x]?"YES\n":"NO\n");
	}

}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

H 线性变换

题意:

给你 \(n,P,K,B,T\) ,以及长度为 \(n\) 的整数序列:\(a_1,a_2,..,a_n\)

同时你有一个加密值 \(X\) ,初始值为 \(0\)

你需要进行 \(T\) 次操作,每次操作进行如下变换:

  • \(X=x+a_p\)
  • \(P=(K*P+B)\%n\)

做法:

首先我们发现 \(T\ (0\leq T\leq10^{12})\)

我们不可能暴力求解,但我们发现这是一个经典的式子:\(P=(K*P+B)\%n\)

由于 \(\%\) 的存在,这个式子是存在循环节,由于模数 \(1\leq n\leq10^6\)

所以我们可以暴力循环找到这个循环节:

这里给出查找类似上述式子循环节的方法:

const int N=1e6+10;
int vis[N],tp;
int P;
while(!vis[N]){
	vis[P]=++tp;
	P=(P*K+B)%n;
}

我们用一个类似时间戳的想法来找到这一段循环节,我们可以举一个例子:

\(1,(2,3,4,0,7,5,9),(2,3,4,0,7,5,9).......\)

我们发现循环节为:\((2,3,4,0,7,5,9)\)

由于我们是记录时间戳,当我们发现 \(vis[p]!=0\),说明这个数以及出现过,我们只需要找到第一次 \(vis[p]\) 出现的位置,就可以找到整段循环节

循环节长度就为:\(tp-vis[p]+1\)

故回到这个题,我们只需要找到循环节,然后我们假设循环节出现的位置是:\(l\),我们只需要计算 \(1\backsim l-1\) 的和,以及一个完整循环节:\([l,r]\) 的和

我们就可以知道在 \(T\) 次后,变换的结果:

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 

const int N=1e6+10;
int a[N];
int vis[N]; //找循环节
int v[N]; //记一下对应时间的p

void solve(){
	int n,P,K,B;
	LL T;
	cin>>n>>P>>K>>B>>T;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	LL val=0,tp=0;
	while(!vis[P]){
		vis[P]=++tp;
		val+=a[P];
		v[tp]=P;
		if(T==tp){
			cout<<val<<'\n';
			return ;
		}
		P=(1LL*P*K+B)%n;
	}

	LL res=0;
	int l=vis[P],r=tp;
	for(int i=1;i<l;i++){
		res+=a[v[i]];
	}
	val-=res;
	T-=l-1;
	LL ans=res+val*(T/(r-l+1));
	T%=(r-l+1);
	for(int i=1;i<=T;i++){
		ans+=a[v[l+i-1]];
	}
	cout<<ans<<'\n';
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

I 试题排版

题意:

给你一个整数 \(m(1\leq m\leq5000)\),问你从 \(1\backsim m\)这些数选,凑出和恰好为 \(m\) 的方案有几种,每个数可以重复选

做法:

完全背包裸体,凑体积恰好为 \(m\) 问题

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 

const int N=5100;
const int mod=998244353;
int f[N];

void solve(){
	
	int m;
	cin>>m;
	f[0]=1;
	for(int i=1;i<=m;i++){
		for(int j=i;j<=m;j++){
			f[j]+=f[j-i];
			f[j]%=mod;
		}
	}

	cout<<f[m]<<'\n';

}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

J QQ群

题意:

你有 \(n+1\) 个点,编号从 \(0\backsim n\),它们之间存在一些有向边的关系,现在定义如下的点为怨种:

  • 若点 \(u\) 在一个环(自环也算)内,则它是一个怨种
  • 编号为 \(0\) 的点一开始就是怨种

现在你可以连一条从 \(0\) 号点指向任意点的有向边,然后按照这个边走下去,在链上的所有点都将成为怨种

请问图中怨种数量最多是多少?

做法:

由于是有向图,且涉及环,我们可以考虑用 \(tarjan\) 求出强连通分量,然后缩点,这样每一个强连通分量内部的点

\(sz[i]>1\) ,那么这个强连通分量内的所有点都一定是怨种

\(sz[i]=1\),但这个点有连向自己的边,即存在自环,那么也算怨种

现在我们缩完点之后,图就成了拓扑图,我们只需要在图上找一条最长链(使得链上的孤立点最多)即可

注意我们每一个强连通分量是孤立点当且仅当,它的\(sz[i]=1\),且它不存在自环

所以我们只要在图上 \(dp\) 一遍即可

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 


const int N=1e4+10;
vector<int>g[N],G[N];
int dfn[N],low[N],stk[N],id[N],sz[N];
bool cire[N];
int top,scc,tis;
bool is_stk[N];
int f[N];
bool me[N]; //自环

void tarjan(int u){
	dfn[u]=low[u]=++tis;
	stk[++top]=u,is_stk[u]=true;
	for(auto v:g[u]){
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(is_stk[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(dfn[u]==low[u]){
		int y;
		scc++;
		do{
			y=stk[top--];
			is_stk[y]=false;
			id[y]=scc;
            cire[y]|=me[y];
			sz[scc]++;
		}while(y!=u);
	}
}

void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		g[i].push_back(x);
        if(i==x)me[i]=true;
	}
	for(int i=0;i<=n;i++){
		if(!dfn[i])tarjan(i);
	}

	for(int i=0;i<=n;i++){
		for(auto j:g[i]){
			if(id[j]!=id[i]){
				G[id[i]].push_back(id[j]);
			}
		}
	}
	memset(f,-1,sizeof f);
	for(int i=scc;i>=1;i--){
		if(f[i]==-1)f[i]=sz[i]==1&&!cire[i]==1?1:0;
		for(auto j:G[i]){
			f[j]=max(f[j],f[i]+(sz[j]==1&&!cire[j]&&j!=1?1:0));
		}
	}

	int ans=0,res=0;
	for(int i=1;i<=scc;i++){
		if(i!=1&&(sz[i]>1||sz[i]==1&&cire[i]))ans+=sz[i];
		res=max(res,f[i]);
	}
	//cout<<ans<<' '<<res<<endl;
	cout<<ans+res+1<<'\n';

}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

K 跳跃

题意:

给你一个数轴有 \(n\) 个点,你从 \(1\) 号点开始,每个点都有一个区间 \([l_i,r_i]\),每次你会等概率的从这个区间里面选一个数 \(x\),若\(i+x>n\) 游戏结束,否则

你会跳到 \(i+x\)的位置,每跳到一个点都会获得 \(a_i\) 的金币,现在问你最后获得的金币期望是多少?

做法:

期望\(dp\)题,我们考虑从终点状态往前推,由于所有 \(>n\) 的位置都是游戏结束点,我们可以把这些点都归到点\(n+1\)

这样我们就可以定义:\(dp[i]\) 表示从 \(i\) 跳到 \(n+1\) 号点所获得的金币期望

则:\(dp[n+1]=0\)

然后我们只要倒推,很容易得到状态转移:

\(dp[i]=(dp[i+l_i]+dp[i+li+1]+.....+dp[i+r_i])*\large \frac{1}{r_i-l_i+1}+a[i]\)

我们发现 \(dp[i+l_i]+...+dp[i+r_i]\) 就是一个长度为 \(r_i-l_i+1\) 的后缀和,我们可以利用后缀和优化掉这个复杂度,这样我们的 \(dp\) 的复杂度就控制到了\(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 

const int N=2e5+10;
const int mod=998244353;

int dp[N]; //表示从i跳到n+1的总金币期望
int a[N],l[N],r[N];
int suf[N]; //后缀和


int MOD(int x){
	return (x%mod+mod)%mod;
}

LL qsm(LL a,LL b){
	LL res=1;
	while(b){
		if(b&1)res=res*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return res;
}

void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>l[i];
	}
	for(int i=1;i<=n;i++){
		cin>>r[i];
	}

	for(int i=n;i>=1;i--){
		LL inv=qsm(r[i]-l[i]+1,mod-2);
		dp[i]=MOD(suf[min(n+1,i+l[i])]-suf[min(n+1,i+r[i]+1)])*inv%mod;
		dp[i]=(dp[i]+a[i])%mod;
		suf[i]=(suf[i+1]+dp[i])%mod;
	}

	cout<<dp[1]<<'\n';

}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

L 我把你背回来的

题意:

给你两张一模一样的图,都有 \(n\) 个点 \(m\) 条无向边,但来两个图的边权不一样

现在你需要重新给图定义边权,规则如下:

  • 对于两个图分别来说边 \(u-v\) 若从\(u\)\(n\) 的最短路都经过这条边,则新图边权为 \(0\)
  • 若只有一个图满足,则边权为 \(1\)
  • 若两个图都不满足,则边权 \(2\)

然后问你在新图中从 \(1\) 走到 \(n\) 的最短路径是多少?

做法:

首先由于是无向图,我们可以求一遍从 \(n\) 出发走到各个点的最短路,这样我们就可以建图了:

对于某条边 \(u->v\),我们只要看\(d_1[u]\), 与 \(d_1[v]+r\),以及\(d_2[u]\)\(d_2[v]+d\) 的相等关系就可以建图了

然后建完图,跑一次 \(dijstla\) 即可

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 
typedef pair<int,int>PII;
const int N=4e5+10;
const int INF=0x3f3f3f3f;
vector<PII>g1[N],g2[N],g3[N];
int d1[N],d2[N],d3[N];
bool st[N];
struct node{
	int a,b,r,d;
}edge[N];


void dijstla(int s,int d[],vector<PII>g[],int sz){
	for(int i=1;i<=sz;i++){
		st[i]=false,d[i]=INF;
	}
	d[s]=0;
	priority_queue<PII,vector<PII>,greater<PII>>heap;
	heap.push({0,s});
	while(heap.size()){
		auto [_,u]=heap.top();
		heap.pop();
		if(st[u])continue;
		st[u]=true;
		for(auto [v,w]:g[u]){
			if(d[v]>d[u]+w){
				d[v]=d[u]+w;
				heap.push({d[v],v});
			}
		}
	}
}

void solve(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		auto &[a,b,r,d]=edge[i];
		cin>>a>>b>>r>>d;
		g1[a].push_back({b,r});
		g1[b].push_back({a,r});
		g2[a].push_back({b,d});
		g2[b].push_back({a,d});
	}
	dijstla(n,d1,g1,n);
	dijstla(n,d2,g2,n);

	for(int i=1;i<=m;i++){
		auto [a,b,r,d]=edge[i];
		if(d1[b]==d1[a]+r&&d2[b]==d2[a]+d){
			g3[b].push_back({a,0});
		}else if(d1[b]==d1[a]+r){
			g3[b].push_back({a,1});
		}else if(d2[b]==d2[a]+d){
			g3[b].push_back({a,1});
		}else{
			g3[b].push_back({a,2});
		}
		swap(a,b);
		if(d1[b]==d1[a]+r&&d2[b]==d2[a]+d){
			g3[b].push_back({a,0});
		}else if(d1[b]==d1[a]+r){
			g3[b].push_back({a,1});
		}else if(d2[b]==d2[a]+d){
			g3[b].push_back({a,1});
		}else{
			g3[b].push_back({a,2});
		}
	}
	dijstla(1,d3,g3,n);
	cout<<d3[n]<<'\n';
}


int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

M 粉色头发的可爱女孩

题意:

\(k\) \((1\leq k\leq20)\) 种标签,每个人都有自己的标签\(v_1,v_2...v_m\),同时拥有自己独一无二的魅力值

现在有 \(Q\) 次询问,每次询问给定一些标签:\(x_1,x_2,...x_m\) ,你需要找到同时满足这些标签的人中,魅力值最大的那个人的编号,如果没有输出\(-1\)即可

做法:

首先抽象题意,需要我们找到同时满足拥有 \(x_1,x_2,..x_m\) 这些标签的人中魅力值最大的,由于 \(k\) 不是很大

我们可以考虑状态压缩 \(f[1<<k]\) 来表示,标签的集合情况,然后我想想如何求特定的这些值

首先我们来举一个例子,比如我们现在需要更新 \(f[\{1,4,5\}]\) ,那么我们可以发现大集合是包含小集合

即对于满足 \(\{1,4,5,x.y.z....\}\) 的这些大集合,它们都满足同时含有 \(1,4,5\) 所以它们都可以来更新 \(f[\{1,4,5\}]\)

故我们发现它本质是一个递推问题,类似状态压缩\(dp\)

\(f[\{1,4,5\}]=max(f[\{1,4,5\}],f[\{1,4,5,x,,,,,,,\}],f[\{1,4,5,y,z...\}]...............)\)

故我们只要从大到小递推即可:

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 
typedef pair<int,int>PII;

const int N=21;
int f[1<<N],id[1<<N];

void solve(){
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		int x,m;
		cin>>x>>m;
		int state=0;
		for(int j=0;j<m;j++){
			int p;
			cin>>p;
			p--;
			state|=1<<p;
		}
		if(f[state]<x){
			f[state]=x;
			id[state]=i;
		}
	}

	//倒推
	for(int i=(1<<k)-1;i>=1;i--){
		for(int j=0;j<k;j++){
			if(i>>j&1){
				int s=i^(1<<j);
				if(f[s]<f[i]){
					f[s]=f[i];
					id[s]=id[i];
				}
			}
		}
	}

	int q;
	cin>>q;
	while(q--){
		int x;
		cin>>x;
		int state=0;
		for(int i=1;i<=x;i++){
			int p;
			cin>>p;
			p--;
			state|=1<<p;
		}
		if(!id[state]){
			cout<<"OMG!\n";
		}else{
			cout<<id[state]<<"\n";
		}
	}
}


int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(10);
    
    int t;
    t=1;
    //cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}
posted @ 2023-02-06 21:41  jackle  阅读(30)  评论(0编辑  收藏  举报