一道挺有意思的思维题(?)
首先我们假设根节点深度为 (0),那么 Daniel 的目标显然就是堵住一些节点使得 Stjepan 不能移动到深度为 (k) 的节点,Stjepan 的目标就是将棋子移到深度为 (k) 的节点。我们还可以发现一个显然的性质,就是 Daniel 在第 (i) 步肯定会堵住深度为 (i) 的节点(如果还存在深度为 (i) 的节点没有堵住),因为如果堵住一个深度 (<i) 的节点那显然是无效的,而如果堵住深度 (>i) 的节点,我们完全可以将其移到它深度为 (i) 的祖先上,这样肯定比堵住原来的节点来得更优。
于是现在问题就转化为,你需要选择一个集合 (Ssubseteq T={1,2,3,cdots,k}) 并堵住深度为 (x) 的点各一个((xin S)),使得不存在深度为 (k) 的点满足从根节点到该点的路径都被堵住了。
这样看上去还是很不好做,不过注意到当 (k) 比较大的时候答案都是 DA
,事实上,对于 (kgesqrt{n}) 答案必定是 DA
,感性理解(因为我也不会严谨证明,想了半天没想通/wul)可知最劣情况大概是根节点下面接了 (k) 条长度为 (k) 的链,那么此时你只需在第 (i) 条链上堵住长度为 (i) 的边即可,这样我们就将 (k) 的规模降到了 (19)。
注意到这道题的状态与集合有关,因此考虑状压 (dp),由于每次堵住一个点会使得一个子树内的点到根节点的路径受阻,因此考虑 DFS 序,将所有深度为 (k) 的点按照 DFS 序从小到大排成一列,那么堵住每个点后会使一段区间 ([L_x,R_x]) 内的深度为 (k) 的点不可到达,因此可以设 (dp_{i,S}) 表示 (1sim i) 的点已经被堵住了,堵住的点的深度组成的集合为 (S) 是否合法,转移就枚举 (x) 满足 (L_x=i+1) 且 (dep_x otin S),然后用 (dp_{i,S}) 更新 (dp_{R_x,Scup{dep_x}}) 即可。
时间复杂度 (n2^k)。
const int MAXN=400;
int n,k,hd[MAXN+5],to[(MAXN<<1)+5],nxt[(MAXN<<1)+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dep[MAXN+5],L[MAXN+5],R[MAXN+5],lcnt=0;bool on[MAXN+5];
void dfs(int x,int f){
on[x]=1;
if(dep[x]>=k) return L[x]=lcnt,R[x]=++lcnt,void();
L[x]=lcnt;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dep[y]=dep[x]+1;dfs(y,x);
} R[x]=lcnt;
}
vector<pii> idl[MAXN+5];
bool dp[MAXN+5][1048577];
int main(){
scanf("%d%d",&n,&k);if(k*k>=n) return printf("DA
"),0;
for(int i=1,u,v;i<n;adde(u,v),adde(v,u),i++) scanf("%d%d",&u,&v);
dfs(1,0);dp[0][0]=1;
for(int i=1;i<=n;i++) if(on[i]&&(i^1)) idl[L[i]].pb(mp(R[i],dep[i]));
for(int i=0;i<lcnt;i++) for(int j=0;j<(1<<k);j++)
for(pii p:idl[i]) if(~j>>(p.se-1)&1) dp[p.fi][j|(1<<p.se-1)]|=dp[i][j];
bool ret=0;for(int i=0;i<(1<<k);i++) ret|=dp[lcnt][i];
printf("%s
",ret?"DA":"NE");
return 0;
}