1. 程式人生 > 其它 >P6499 [COCI2016-2017#2] Burza 題解

P6499 [COCI2016-2017#2] Burza 題解

Luogu

Description.

Alice Bob 打隔膜,有一棵 \(n\) 個點的樹,最初棋子在 \(1\) 節點。
Alice 刪掉一個節點,然後 Bob 移動棋子到一個沒到過的地方。
Alice 不知道 Bob 的每次操作
問 Alice 有無必勝策略

\(n\le 400\)

Solution.

首先發現 \(k\) 增加時無解情況要求的葉子增加葉子深度也增加。
構造一個無解情況,就需要 \(k\) 條深度為 \(k\) 的鏈,點數是 \(k^2\) 級別的。
所以如果 \(k^2\ge n\),那肯定有解了。
所以 \(k\) 的範圍變成了 \(20\)

每次貪心策略肯定是第 \(i\)

輪刪掉一個深度為 \(i\) 的點(根為 \(0\)),這樣肯定最優。
因為 Bob 操作不確定,所以我們肯定要把所有深度為 \(K\) 的點刪完。
然後可以考慮 dfn dp,就把 dfn 拉出來,然後直接跑狀壓 dp。
狀壓 dp 記錄當前選了深度為多少的點,直接轉移就行了。

Coding.

點選檢視程式碼
//是啊……你就是那隻鬼了……所以被你碰到以後,就輪到我變成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=405;struct ${int d,w;};vector<$>v[N];
struct edge{int to,nxt;}e[N<<1];int n,K,et,head[N],lim;
int dep[N],lf[N],rg[N],dt;char dp[N][524289],vs[N];
inline void adde(int x,int y) {e[++et]=(edge){y,head[x]},head[x]=et;}
inline void dfs(int x,int fa)
{
	vs[x]=1,lf[x]=dt;if((dep[x]=dep[fa]+1)==K) return rg[x]=++dt,void();
	for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) dfs(e[i].to,x);
	rg[x]=dt;
}
int main()
{
	read(n,K);for(int i=1,x,y;i<n;i++) read(x,y),adde(x,y),adde(y,x);
	if(K*K>=n) return puts("DA"),0;else dep[0]=-1,dfs(1,0),dp[0][0]=1,lim=(1<<K)-1;
	for(int i=1;i<=n;i++) if(vs[i]&&i!=1) v[lf[i]].push_back(($){dep[i]-1,rg[i]});
	//for(int i=0;i<=n;i++) for($ j:v[i]) printf("%d %d : %d\n",i,j.w,j.d);
	for(int i=0;i<dt;i++) for(int j=0;j<=lim;j++) if(dp[i][j])
		for(auto x:v[i]) if(!((j>>x.d)&1)) dp[x.w][j|1<<x.d]=1;
	char rs=0;for(int i=0;i<=lim;i++) rs|=dp[dt][i];
	return puts(rs?"DA":"NE"),0;
}