zoukankan      html  css  js  c++  java
  • 【CF613D】Kingdom and its Cities 虚树+树形DP

    【CF613D】Kingdom and its Cities

    题意:给你一棵树,每次询问给出k个关键点,问做多干掉多少个非关键点才能使得所有关键点两两不连通。

    $n,sum kle 10^5$

    题解:刷虚树板子啦!

    首先如果两个关键点相邻则无解。然后建出虚树,进行树形DP。设f[i]表示i子树中的关键点都不连通,且i子树中的点与外面的点也不连通的最小花费,g[i]表示i子树重的关键点都不连通,且子树中只有一个点与外面的点连通的最小花费。转移时讨论一波即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn=100010;
    int n,m,now,cnt,top;
    int to[maxn<<1],nxt[maxn<<1],head[maxn],fa[19][maxn],Log[maxn],dep[maxn],A[maxn],vis[maxn],st[maxn],f[maxn],g[maxn],p[maxn],q[maxn];
    vector<int> ch[maxn];
    void dfs(int x)
    {
    	p[x]=++q[0],q[q[0]]=x;
    	for(int i=head[x];i!=-1;i=nxt[i])	if(to[i]!=fa[0][x])	fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,dfs(to[i]);
    }
    inline void add(int a,int b)
    {
    	to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
    }
    bool cmp(const int &a,const int &b)
    {
    	return p[a]<p[b];
    }
    inline int lca(int a,int b)
    {
    	if(dep[a]<dep[b])	swap(a,b);
    	for(int i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	a=fa[i][a];
    	if(a==b)	return a;
    	for(int i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	a=fa[i][a],b=fa[i][b];
    	return fa[0][a];
    }
    void DP(int x)
    {
    	int y,t0=0,t1=0,t2=0;
    	vector<int>::iterator it;
    	for(it=ch[x].begin();it!=ch[x].end();it++)
    	{
    		y=*it,DP(y);
    		t2=min(t2+f[y],t0+min(f[y],g[y])),t0+=f[y],t1+=min(f[y],g[y]);
    	}
    	ch[x].clear();
    	if(vis[x]==now)
    	{
    		g[x]=t0,f[x]=t0+1;
    	}
    	else
    	{
    		g[x]=t2,f[x]=min(t1+1,t0);
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	//freopen("a.in","r",stdin);
    	n=rd();
    	int i,j,a,b,c;
    	memset(head,-1,sizeof(head));
    	for(i=2;i<=n;i++)	a=rd(),b=rd(),add(a,b),add(b,a),Log[i]=Log[i>>1]+1;
    	dep[1]=1,dfs(1);
    	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]];
    	m=rd();
    	for(now=1;now<=m;now++)
    	{
    		a=rd();
    		for(i=1;i<=a;i++)	A[i]=rd(),vis[A[i]]=now;
    		for(i=1;i<=a;i++)	if(vis[fa[0][A[i]]]==now)
    		{
    			puts("-1");
    			break;
    		}
    		if(i<=a)	continue;
    		sort(A+1,A+a+1,cmp);
    		st[top=1]=A[1];
    		for(i=2;i<=a;i++)
    		{
    			c=lca(A[i-1],A[i]);
    			while(top&&dep[st[top]]>dep[c])
    			{
    				b=st[top--];
    				if(top&&dep[st[top]]>dep[c])	ch[st[top]].push_back(b);
    				else	ch[c].push_back(b);
    			}
    			if(!top||st[top]!=c)	st[++top]=c;
    			st[++top]=A[i];
    		}
    		while(top>1)	ch[st[top-1]].push_back(st[top]),top--;
    		DP(st[top]);
    		a=min(g[st[top]],f[st[top]]);
    		printf("%d
    ",a);
    	}
    	return 0;
    }
  • 相关阅读:
    jdk jre jvm 关系
    深入Android开发之--理解View#onTouchEvent
    使用android.view.TouchDelegate扩大View的触摸点击区域
    Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习
    BitMap 内存使用优化
    android——屏幕适配大全(转载)
    android——ImageLoader添加缓存
    android——混淆打包
    android——仿微拍贷滑动圆形菜单
    android——使用自带录屏工具进行屏幕录像
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8723947.html
Copyright © 2011-2022 走看看