preparing

QZHWTEST2021.5.23分析

树上游戏

题面

题目描述

\(FLY\)和朋友玩一个游戏。
在一棵树上,每个点都有一个点权,\(FLY\)和朋友从根开始,轮流取出点权作为分值,并且由当前玩家选择前往哪一个儿子,直到到达叶子节点后计算总分,总分大的胜。
\(FLY\)和他的朋友都非常谨慎,能够每次选择儿子都做到理论上的最优值,请你判断游戏是否有先手必胜的策略。

输入

第一行输入数据组数\(T\)
第二行输入树的点数\(n\)
第三行\(n\)个数字,为每个点的点权
接下来\(n-1\)行输入树边\(u\)\(v\)

输出

若先手必胜,则输出\(win\)
若先手必败,则输出\(loss\)
若必定平局,则输出\(draw\)

样例输入

2
5
3 4 2 3 2
1 2
2 3
2 4
4 5
5
4 1 4 3 3
1 2
2 3
3 4
4 5

样例输出

draw
win

思路

看到题目,直接打爆搜,回溯时优化成树形\(DP\),复杂度\(O(NT)\)
在搜索时,记录当前节点的深度\(depth\),这可以帮助我们知道现在轮到谁了。我用的是\(flag\)记录,对应意思代码中有。
每个节点搜索每一个孩子,一直搜到叶子。到叶子后,判断此时先后手谁分多,回溯到上一个节点,最后输出结果即可。
但是你会发现,样例的第一组都过不去。原因是这样的:

由图,在节点\(2\)时,如果向\(3\)走,先手胜;如果向\(4\)走,平局。因为此时是后手选,根据最优策略,故他一定会选\(4\)。因此,我们要在每次回溯时判断是谁选择,先手应取尽量好的,后手应取对后手尽量好的。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100005
using namespace std;
int t,n,ans;//ans=0表示输,1表示平,2表示胜 
int a[maxn],uu,vv;
int head[maxn],w=0;
struct gragh{
	int to,nex;
}g[maxn*2];
int tot[2];//0先手,1后手 
void clear(){
	w=0;ans=-1;
	memset(a,0,sizeof(a));
	memset(g,0,sizeof(g));
	memset(tot,0,sizeof(tot));
	memset(head,0,sizeof(head));
}
void add(int x,int y){
	g[++w].to=y;
	g[w].nex=head[x];
	head[x]=w;
}
int dfs(int i,int fa,bool flag){
	int anss=-1;
	bool flag2=0;
	tot[flag]+=a[i];
	for(int j=head[i];j!=0;j=g[j].nex){
		if(g[j].to==fa){
			continue;
		}
		flag2=1;
		int x=dfs(g[j].to,i,!flag);
		if(!flag){//FLY
			anss=(anss==-1?x:max(x,anss));
		}else{
			anss=(anss==-1?x:min(x,anss));
		}
	}
	if(!flag2){//叶子 
		if(tot[0]>tot[1]){
			anss=2;
		}else if(tot[0]==tot[1]){
			anss=1;
		}else{
			anss=0;
		}
	} 
	tot[flag]-=a[i];
	return anss;
}
int main(){
	scanf("%d",&t);
	while(t--){
		clear();
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		for(int i=1;i<n;i++){
			scanf("%d%d",&uu,&vv);
			add(uu,vv);
			add(vv,uu);
		}
		ans=dfs(1,-1,0);
		if(ans==0){
			printf("loss\n");
		}else if(ans==1){
			printf("draw\n");
		}else{
			printf("win\n");
		}
	}
	return 0;
}

无线通讯网 & 洛谷P1991

题面

题目描述

国防部计划用无线网络连接若干个边防哨所。\(2\)种不同的通讯技术用来搭建无线网络;
每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。
任意两个配备了一条卫星电话线路的哨所(两边都有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过\(D\),这是受收发器的功率限制。收发器的功率越高,通话距离\(D\)会更远,但同时价格也会更贵。
收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个\(D\)。你的任务是确定收发器必须的最小通话距离\(D\),使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。

输入

\(1\)行:\(2\)个整数\(S\)\(1\le S\le 100\))和\(P\)\(S<P\le 500\)),\(S\)表示可安装的卫星电话的哨所数,\(P\)表示边防哨所的数量。
接下里\(P\)行,每行描述一个哨所的平面坐标(\(x,y\)),以\(km\)为单位,整数,\(0\le x,y\le 10000\)

输出

\(1\)个实数\(D\),表示无线电收发器的最小传输距离。精确到小数点后两位。

样例输入

2 4
0 100
0 300
0 600
150 750

样例输出

212.13

思路

题目中说到“直接或者间接连通”与“求最小通话距离”,这很容易让我们想到最小生成树。枚举每两个点,计算出距离\(dis\),然后排序,建立最小生成树,输出最大边即可。
需注意的是,题中有给到 卫星电话 这个东西,可以任两点无条件直接连通。考虑到最小生成树要连接\(n-1\)条边,而 卫星电话 可以随便连\(s-1\)条边,故我们只需要枚举最小的\(n-s\)(即\(n-1-(s-1)\))条边即可,剩下的\(s-1\)条较长的边用 卫星电话 即可。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxp 505 
using namespace std;
int s,p;
struct node{
	int x,y;
}a[maxp];
int w=0;
struct line{
	int x,y;
	double len;
}l[maxp*maxp];
double dis(int p1,int p2){
	return sqrt(pow(a[p1].x-a[p2].x,2)+pow(a[p1].y-a[p2].y,2));
}
bool cmp(line a,line b){
	return a.len<b.len;
}
int f[maxp];
int find(int x){
	if(f[x]==x){
		return x;
	}
	return f[x]=find(f[x]);
}
int main(){
	scanf("%d%d",&s,&p);
	for(int i=1;i<=p;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		f[i]=i;
	}
	for(int i=1;i<=p;i++){
		for(int j=1;j<i;j++){
			l[++w].x=i;
			l[w].y=j;
			l[w].len=dis(i,j);
		}
	}
	sort(l+1,l+1+w,cmp);
	int tot=0;
	double ans=-1;
	for(int i=1;i<=w;i++){
		int r1=find(l[i].x),r2=find(l[i].y);
		if(r1!=r2){
			f[r2]=r1;
			tot++;
			ans=l[i].len;
		} 
		if(tot==p-s){
			printf("%.2f",ans);
			return 0;
		}
	}
	return 0;
}
/*
2 4
0 100
0 300
0 600
150 750
*/

仙人掌 & 洛谷P3687

题面

题目描述

如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌。所谓简单环即不经过重复的结点的环。

现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得这张图中的边数太少了,所以她想要在图上连上一些新的边。同时为了方便的存储这张无向图,图中的边数又不能太多。
经过权衡,她想要加边后得到的图为一棵仙人掌。
不难发现合法的加边方案有很多,可怜想要知道总共有多少不同的加边方案。
两个加边方案是不同的当且仅当一个方案中存在一条另一个方案中没有的边。

输入

多组数据,第一行输入一个整数\(T\)表示数据组数。
每组数据第一行输入两个整数\(n\)\(m\),表示图中的点数与边数。
接下来\(m\)行,每行两个整数\(u\)\(v\)(\(1\le u,v\le n\)\(u\ne v\)) 表示图中的一条边。保证输入的图联通且没有自环与重边。

输出

对于每组数据,输出一个整数表示方案数,当然方案数可能很大,请对\(998244353\)取模后输出。

样例输入

2
3 2
1 2
1 3
5 4
1 2
2 3
2 4
1 5

样例输出

2
8

思路

代码

posted @ 2021-06-19 13:17  qzhwlzy  阅读(83)  评论(0)    收藏  举报