首页 > 其他分享 >CF1442F Differentiating Games

CF1442F Differentiating Games

时间:2023-03-18 16:56:45浏览次数:57  
标签:DAG 20 ll CF1442F 我们 棋子 Games 类点 Differentiating

CF1442F Differentiating Games

传送门

CF1442F Differentiating Games

题目大意

给你一个 DAG,\(n(n \le 1000)\) 个点,\(m(m \le 10^5)\) 条边。一次游戏为:两人轮流操作,每次可以选择一个硬币,向着某一条出边移动一步,不能操作者输。

你可以在最开始的时候修改 \(k(k \le 4242)\) 次这个图,每次可以加一条边或者删一条边,修改后可以不是 DAG。

有 \(T\) 次询问。每次交互库会选择一个隐藏节点 \(X\)。然后,你可以询问 \(t\) 次,每次给交互库一个可重集 \(S(\sum |S| \le 20)\),交互库会告诉你:在这些位置各放一个棋子,再在 \(X\) 放一个棋子,那么游戏的结果是先手胜、先手负或是平局。你需要猜出 \(x\)。

思路

既然要删边加边,可以考虑满足哪些条件的图能保证猜出 \(X\)。

可以发现,如果图是一个完全 DAG,那么我们每次询问一个点:

  1. 这个点是 \(X\),那么询问这个位置就表示在这个位置上有两个棋子。后手跟着先手走,先手必败。

  2. 这个点不是 \(X\),因为是完全 DAG,先手可以一步变为两个棋子在同一个位置上。这时先手必胜。

虽然这样我们需要 \(O(n^2)\) 条边,但是我们只需要查询 \(n\) 次。题目要求查询 \(20\) 次,那我们是否可以建一个大小为 \(20\) 的完全 DAG 呢?

如果我们有一个大小为 \(20\) 的完全 DAG,那么首先我们如果发现一个点先手必败,显然是 \(X\)。

如果没有先手必败,那么每个点的答案就有两种情况,先手必胜或平局。

那么总的情况就有 \(2^{20}=1048576\) 个,能够满足 \(n \le 1000\) 的需求。

那么我们要如何构造图剩下的部分呢?

为了方便,我们将完全 DAG 中的点称作 \(A\) 类点,其它的称作 \(B\) 类点。

因为我们已经判断出了 \(X\) 是 \(A\) 类点的情况,所以现在我们只需要讨论 \(X\) 是 \(B\) 类点。

我们给所有 \(B\) 类点都建一个自环,并让所有 \(B\) 类点有边连向 \(A\) 类点。因为这样之后,对于先手来说,要么把 \(X\) 上的棋子走出自环,要么原地不动。如果先手走出去是必败状态,一定会选择不动。如果选择不动,那么后手拿到的情况与先手相同,一样会选择不动,就产生了平局。否则先手肯定必胜。这就符合我们所需要的 每个点的答案有两种情况,先手必胜或平局

如果我们可以知道什么时候先手必胜,那么我们就可以构造边使得每个点的获胜情况两两不同。

那么什么时候先手必胜?

我们假设先手已经将棋子从 \(B\) 类点移到了 \(A\) 类点,那么这时两个棋子都在 \(A\) 类点中,也就是跟我们之前讨论的相同。

也就可以推出,如果 \(B\) 类点上的这个棋子能够直接到达另一个棋子的位置,那么先手必胜。

这样,我们让每个点的获胜情况两两不同,只需要让连向的 \(A\) 类点组成的集合两两不同即可。

现在我们需要让修改次数最小。这里我们取 \(n=1000\) 计算。

首先我们选出 \(A\) 类点。我们可以发现,选择拓扑序中最后的一些点能够使 \(A\) 类点到 \(B\) 类点没有边。

所以我们可以选择拓扑序中的后 \(20\) 个点作为 \(A\) 类点。那么连完全 DAG 的边就是 \(\frac{(1+19) \times 19}{2} = 190\)。给 \(B\) 类点连自环需要 \(980\) 条边。

为了让 \(B\) 类点到 \(A\) 类点的边更改数尽可能小,我们以更改的次数作为状态的排序依据,这个可以看代码理解。连边个数大约为 \(C_{20}^1 \times 1 + C_{20}^2 \times 2 + 770 \times 3 = 2710\)

总边数为 \(190+980+2710=3880\),可以通过本题。

代码实现

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
#define pb push_back
const ll N=4e3+10;
const ll M=2e6+10;
using namespace std;

ll cnt;//改变的边的数量
struct EDGE{
	ll x,y;//起终点
	ll opt;//0为删的边,1为连的边
}edge[M];//记录改变的边

ll n,m,k;
ll a[M];
vector<ll>e[M];//存边
ll vis[N][N];//快速判断是否有边
ll in[M];//入度
ll tot,tp[M],b[M];//tp是拓扑序,b是拓扑序中对应的位置
ll cntA,node[M];//A类点个数及编号,node[i]为0表示为B类点
ll book[M];//加边时判断状态是否能到达
ll ans[M];//记录答案点

void __top(){//拓扑序板子
	queue<ll>q;
	For(i,1,n)if(!in[i])q.push(i);
	while(!q.empty()){
		ll x=q.front();
		q.pop();
		tp[++tot]=x,b[x]=tot;//记录拓扑序
		for(ll y:e[x]){
			in[y]--;
			if(!in[y])q.push(y);
		}
	}
}

void find_A(){//找A类点
	cntA=0;
	Rep(i,tot,1){
		node[tp[i]]=++cntA;
		if(cntA>=20)break;
	}
}

void change(ll x,ll y,ll opt){//flag=0删边,flag=1加边
	edge[++cnt]=(EDGE){x,y,opt};
}

void changeA(){//删无用边,连出完全拓扑图
	For(x,1,n){
		if(!node[x])continue;//跳过B类点
		//删除不符合条件的边
		for(ll y:e[x]){
			if(node[y])continue;//A->A不需要删
			if(b[x]<b[y])continue;//A->B中拓扑序正确的不删
			change(x,y,0);//删边
		}
		//连成完全拓扑图
		For(y,1,n){
			if(!node[y])continue;//跳过B类点
			if(b[x]>=b[y])continue;//拓扑序不正确不连
			if(vis[x][y])continue;//有边不连
			change(x,y,1);//连边
		}
	}
}

void changeB(){//添加自环,调边使答案唯一
	//找出可以调整达到唯一对应一个点的win状态
	queue<ll>q;//状态队列
	vector<ll>tmp;//记录状态
	q.push(0);
	while(q.size()){
		ll s=q.front();//初始状态
		q.pop();
		tmp.pb(s);//记录状态
		For(i,1,cntA){
			ll t=s|(1<<i-1);//下一个状态
			if(!book[t]){
				book[t]=1;
				q.push(t);
			}
		}
	}
	For(x,1,n){
		if(node[x])continue;//跳过A类点
		change(x,x,1);//添加自环
		//状压能到达的A类点
		ll s=0;
		for(ll y:e[x]){
			if(!node[y])continue;//跳过B类点
			s|=1<<node[y]-1;
		}
		//调整边使每个B类点的win状态唯一
		for(ll i:tmp){
			ll t=s^i;//对应的win状态
			if(book[t]){
				ans[t]=x;
				book[t]=0;
				//使当前枚举的win状态唯一对应x
				For(y,1,cntA){
					if(i&(1<<y-1)){
						//因为选的A类点是拓扑序最后的几个,所以tp[tot-y+1]就是对应的编号
						if(s&(1<<y-1))change(x,tp[tot-y+1],0);//删边
						else change(x,tp[tot-y+1],1);//连边
					}
				}
				break;
			}
		}
	}
}

ll query(ll x){
	printf("? 1 %lld\n",x);
	fflush(stdout);
	char str[10];
	scanf("%s",str);
	if(str[0]=='W')return 1;//win
	if(str[0]=='L')return -1;//lose
	return 0;//draw
}

void mian(){
	
	scanf("%lld",&n);
	scanf("%lld",&m);
	scanf("%lld",&k);
	For(i,1,m){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		e[x].pb(y),vis[x][y]=1;//e存边,vis快速判断是否有边
		in[y]++;//统计入度
	}
	
	__top();//拓扑序
	find_A();//找A类点
	changeA();//A类点:删无用边,连出完全拓扑图
	changeB();//B类点:添加自环,调边使答案唯一
	
	//输出改的边
	printf("%lld\n",cnt);
	For(i,1,cnt){
		if(edge[i].opt)printf("+ %lld %lld\n",edge[i].x,edge[i].y);
		else printf("- %lld %lld\n",edge[i].x,edge[i].y);
	}
	fflush(stdout);
	while(k--){
		ll s=0;//记录win状态
		ll flag=0;//标记
		Rep(i,tot,tot-cntA+1){
			ll x=tp[i];//点的编号
			ll answer=query(x);
			if(answer==-1){//lose
				printf("! %lld\n",x);
				fflush(stdout);
				flag=1;//标记
				break;
			}
			if(answer==1){//win
				s|=1<<node[x]-1;//记录状态
			}
		}
		if(!flag){
			printf("! %lld\n",ans[s]);//对应状态的答案
			fflush(stdout);
		}
		char str[10];
		scanf("%s",str);//读掉Correct
	}
	
}

int main(){
	int T=1;
//	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

尾声

如果你发现了问题,你可以直接回复这篇题解

如果你有更好的想法,也可以直接回复!

标签:DAG,20,ll,CF1442F,我们,棋子,Games,类点,Differentiating
From: https://www.cnblogs.com/zsc985246/p/17231171.html

相关文章

  • Games101-Cp1-Transformation
    最近为了求职重新开始把图形学相关的内容重新系统的学习,先把Games101的内容入门,然后把虎书相关的内容补充。Transformation矩阵变换可以对不同坐标系之间进行转换,在这个......
  • How to play Genshin games on Apple Silicon Mac All In One
    HowtoplayGenshingamesonAppleSiliconMacAllInOne如何在Apple芯片的Mac上玩原神游戏macOS&M1/M2CPU✅macOS&IntelCPU❌PlayCoverRuniOSa......
  • GAMES101-HW1-个人遇到的一些问题(1)
    1.环境配置参考:https://blog.csdn.net/euphorias/article/details/120768828这里只简述部分内容:VS2019需要在资源管理器中右键项目-属性-(在对应配置,如Debug下)(1).VC+......
  • [bzoj 3701] Olympic Games (莫比乌斯反演)
    题目描述给出表示一个的格点图,求能够互相看见的点对个数对取模的值.能互相看见定义为此两点连线上没有其他的格点且欧氏距离在[l,r]范围内题目分析首先我们将上下左右相邻......
  • games104 现代游戏引擎 Picoolo环境搭建 vulkan配置
    0前言介绍games104现代游戏引擎课程是由GAMES:GraphicsAndMixedEnvironmentSymposium支持的一个课程。如我们所视,Games并非的含义并不是游戏,而是计算机图形学......
  • GAMES101&Fundamentals of Computer Graphics
    GAMES101-现代计算机图形学入门-闫令琪课程视频:https://www.bilibili.com/video/BV1X7411F744/?share_source=copy_web&vd_source=e9d67ecc6775d595879efd0a7d60d332课程......
  • lupohan44/GamesHub docker版 限免游戏喜加一全家桶
    项目链接:https://github.com/lupohan44/GamesHub前置条件:境外服务器(境内请准备代理),已安装docker电报机器人token使用其他通知方式参考https://github.com/caronc/a......
  • Games
    Acollectionofgames(writtenorco-writtenbyme)1#include<algorithm>//min&max2#include<cmath>3#include<conio.h>4#include<ctime>......
  • [ABC221D] Online games
    [ABC221D]Onlinegames难度:\(832\)标签:差分离散化\(\mathtt{blog}\)有\(n\)个区间\([a_i,a_i+b_i)\),问各被\(1\simn\)个区间覆盖的数字个数有多少个。\(n\le......
  • 偶遇 Trojan.PSW.Win32.OnlineGames.xym,Trojan.Win32.Agent.vvk等
    偶遇Trojan.PSW.Win32.OnlineGames.xym,Trojan.Win32.Agent.vvk等endurer原创2007-09-19 第1版刚才一位网友说他的电脑最近启动一个程序所需时间比较长。让偶通过QQ远程......