bzoj 3611: [Heoi2014]大工程

Description

國家有一個大工程,要給一個非常大的交通網絡裏建一些新的通道。 
我們這個國家位置非常特殊,可以看成是一個單位邊權的樹,城市位於頂點上。 
在 2 個國家 a,b 之間建一條新通道需要的代價爲樹上 a,b 的最短路徑。
 現在國家有很多個計劃,每個計劃都是這樣,我們選中了 k 個點,然後在它們兩兩之間 新建 C(k,2)條 新通道。
現在對於每個計劃,我們想知道:
 1.這些新通道的代價和
 2.這些新通道中代價最小的是多少 
3.這些新通道中代價最大的是多少

Input

第一行 n 表示點數。

 接下來 n-1 行,每行兩個數 a,b 表示 a 和 b 之間有一條邊。
點從 1 開始標號。 接下來一行 q 表示計劃數。
對每個計劃有 2 行,第一行 k 表示這個計劃選中了幾個點。
 第二行用空格隔開的 k 個互不相同的數表示選了哪 k 個點。

Output

輸出 q 行,每行三個數分別表示代價和,最小代價,最大代價。 

Sample Input

10
2 1
3 2
4 1
5 2
6 4
7 5
8 6
9 7
10 9
5
2
5 4
2
10 4
2
5 2
2
6 1
2
6 1

Sample Output

3 3 3
6 6 6
1 1 1
2 2 2
2 2 2

HINT

n<=1000000 


q<=50000並且保證所有k之和<=2*n 

Source


虛樹+樹DP

對於每次詢問建立虛樹

然後樹DP的時候,記錄經過該點的最長,次長,最短,次短鏈。

更新最短答案的時候判斷當前點是否爲詢問點,是的話直接左右找最短,否則左右最短加次短

更新最大答案的時候直接最長加次長

更新總和的時候枚舉每個孩子,看該孩子中有多少關鍵點,統計這些關鍵點對其他子樹上的鏈的貢獻即可

#include<queue>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
struct line
{
	int s,t;
	long long x;
	int next;
}a[2000001],exa[2000001];
int head[1000001],exhead[1000001];
int edge,exedge;
inline void add(int s,int t,long long x)
{
	a[edge].next=head[s];
	head[s]=edge;
	a[edge].s=s;
	a[edge].t=t;
	a[edge].x=x;
}
inline void exadd(int s,int t,long long x)
{
	exa[exedge].next=exhead[s];
	exhead[s]=exedge;
	exa[exedge].s=s;
	exa[exedge].t=t;
	exa[exedge].x=x;
}
bool v[1000001];
int dep[1000001];
int ans[1000001][22];
long long anc[1000001][22];
queue<int> Q,Q1;
inline void bfs(int r)
{
     int i,j;
     dep[r]=1;
     for(i=0;i<=21;i++)
          ans[r][i]=r;
     while(!Q.empty())
          Q.pop();
     Q.push(r);
     v[r]=true;
     while(!Q.empty())
     {
          int d=Q.front();
          Q.pop();
          for(i=head[d];i!=0;i=a[i].next)
          {
               int t=a[i].t;
               if(!v[t])
               {
                    v[t]=true;
                    Q.push(t);
                    dep[t]=dep[d]+1;
                    ans[t][0]=d;
                    anc[t][0]=a[i].x;
                    int dt;
					long long dc;
                    for(j=1;j<=21;j++)
                    {
                    	 dt=ans[t][j-1];
                    	 dc=anc[t][j-1];
                         ans[t][j]=ans[dt][j-1];
                         dc+=anc[dt][j-1];
                         anc[t][j]=dc;
                    }
               }
          }
     }
}
long long ansx;
inline int swim(int x,int y)
{
	 int i=21; 
     while(dep[x]!=dep[y])
     {
          while(dep[ans[y][i]]<dep[x])
               i--;
          ansx+=anc[y][i];
          y=ans[y][i];
     }
     return y;
}
inline long long lca(int x,int y)
{
	 ansx=0;
     if(dep[x]>dep[y])
     {
          int t=x;
          x=y;
          y=t;
     }
     y=swim(x,y);
     int i=21;
     while(x!=y)
     {
          while(ans[x][i]==ans[y][i]&&i!=0)
               i--;
          ansx+=anc[x][i];
          ansx+=anc[y][i];
          x=ans[x][i];
          y=ans[y][i];
     }
     return x;
}
int tot;
int ld[1000001],rd[1000001];
inline void dfs(int d)
{
	tot++;
	ld[d]=tot;
	v[d]=true;
	int i;
	for(i=head[d];i!=0;i=a[i].next)
	{
		int t=a[i].t;
		if(!v[t])
			dfs(t);
	}
	rd[d]=tot;
}
int poi[1000001],stak[1000001];
bool mark[1000001],vx[1000001];
int rt;
inline bool cmp(int x,int y)
{
	return ld[x]<ld[y];
}
inline void create(int k)
{
	int i,j,lc;
	int top=0;
	top++;
	stak[top]=poi[1];
	exedge=0;
	exhead[poi[1]]=0;
	for(i=2;i<=k;i++)
	{
		lc=lca(stak[top],poi[i]);
		if(lc==stak[top])
		{
			exhead[poi[i]]=0;
			top++;
			stak[top]=poi[i];
			if(dep[poi[i]]<dep[rt])
				rt=poi[i];
		}
		else
		{
			int tmp=top;
			while(tmp>0&&dep[stak[tmp]]>dep[lc])
				tmp--;
			tmp++;
			for(j=tmp;j<=top-1;j++)
			{
				lca(stak[j],stak[j+1]);
				exedge++;
				exadd(stak[j],stak[j+1],ansx);
				exedge++;
				exadd(stak[j+1],stak[j],ansx);
			}
			int pretmp=stak[tmp];
			if(tmp==0)
			{
				exhead[lc]=0;
				top=1;
				stak[top]=lc;
				if(dep[lc]<dep[rt])
					rt=lc;
			}
			else if(stak[tmp-1]!=lc)
			{
				exhead[lc]=0;
			//	tmp++;
				stak[tmp]=lc;
				top=tmp;
				if(dep[lc]<dep[rt])
					rt=lc;
			}
			else
				top=tmp-1;
			lca(pretmp,lc);
			exedge++;
			exadd(pretmp,lc,ansx);
			exedge++;
			exadd(lc,pretmp,ansx);
			exhead[poi[i]]=0;
			top++;
			stak[top]=poi[i];
			if(dep[poi[i]]<dep[rt])
				rt=poi[i];
		}
	}
	for(i=1;i<=top-1;i++)
	{
		lca(stak[i],stak[i+1]);
		exedge++;
		exadd(stak[i],stak[i+1],ansx);
		exedge++;
		exadd(stak[i+1],stak[i],ansx);
	}
}
long long sum[1000001];
long long sx[1000001],sx1[1000001],sx2[1000001],sx3[1000001],sx4[1000001];
inline void trdp(int d)
{
	bool flag=false;
	Q.push(d);
	Q1.push(d);
	sum[d]=0;
	sx[d]=0;
	sx1[d]=2100000000;
	sx2[d]=0;
	sx3[d]=0;
	sx4[d]=2100000000;
	v[d]=true;
	int i;
	for(i=exhead[d];i!=0;i=exa[i].next)
	{
		int t=exa[i].t;
		if(!v[t])
		{
			flag=true;
			trdp(t);
			sx[d]+=sum[t]*exa[i].x+sx[t];
			sum[d]+=sum[t];
			if(sx2[t]+exa[i].x>sx2[d])
			{
				sx3[d]=sx2[d];
				sx2[d]=sx2[t]+exa[i].x;
			}
			else if(sx2[t]+exa[i].x>sx3[d])
				sx3[d]=sx2[t]+exa[i].x;
			if(!mark[t])
			{
				if(sx1[t]+exa[i].x<sx1[d])
				{
					sx4[d]=sx1[d];
					sx1[d]=sx1[t]+exa[i].x;
				}
				else if(sx1[t]+exa[i].x<sx4[d])
					sx4[d]=sx1[t]+exa[i].x;
			}
			else
			{
				if(exa[i].x<sx1[d])
				{
					sx4[d]=sx1[d];
					sx1[d]=exa[i].x;
				}
				else if(exa[i].x<sx4[d])
					sx4[d]=exa[i].x;
			}
		}
	}
	if(mark[d])
		sum[d]++;
	if(!flag)
	{
		sx1[d]=0;
		sx4[d]=0;
	}
}
long long ans1,ans2,ans3;
inline void dfsx(int d)
{
	v[d]=true;
	int i;
	long long sumx=0;
	bool flag=false;
	for(i=exhead[d];i!=0;i=exa[i].next)
	{
		int t=exa[i].t;
		if(!v[t])
		{
			flag=true;
			dfsx(t);
			sumx+=(sx[d]-sx[t]-sum[t]*exa[i].x)*sum[t];
			if(mark[d])
				sumx+=sx[t]+sum[t]*exa[i].x;
		}
	}
	ans1+=sumx;
	if(flag)
	{
		if(mark[d])
			ans2=min(ans2,sx1[d]);
		else
			ans2=min(ans2,sx1[d]+sx4[d]);
	}
	ans3=max(ans3,sx2[d]+sx3[d]);
}
inline void getans()
{
	ans1=0,ans2=2100000000,ans3=0;
	dfsx(rt);
	printf("%lld %lld %lld\n",ans1,ans2,ans3);
}
int main()
{
//	freopen("data.in","r",stdin); 
//	freopen("data.out","w",stdout);
	int n;
	scanf("%d",&n);
	int i,j;
	int s,t;
	long long x;
	for(i=1;i<=n-1;i++)
	{
		scanf("%d%d",&s,&t);
		edge++;
		add(s,t,1);
		edge++;
		add(t,s,1);
	}
	bfs(1);
	memset(v,false,sizeof(v));
	dfs(1);
	int m,k;
	scanf("%d",&m);
	memset(v,false,sizeof(v));
	memset(vx,false,sizeof(vx));
	memset(mark,0,sizeof(mark));
	for(i=1;i<=m;i++)
	{
		scanf("%d",&k);
		for(j=1;j<=k;j++)
		{
			scanf("%d",&poi[j]);
			mark[poi[j]]=1;
		}
		sort(poi+1,poi+1+k,cmp);
		rt=poi[1];
		create(k);
		trdp(rt);
		while(!Q1.empty())
		{
			v[Q1.front()]=false;
			Q1.pop();
		}
		getans();
		
		while(!Q.empty())
		{
			v[Q.front()]=false;
			Q.pop();
		}
		for(j=1;j<=k;j++)
			mark[poi[j]]=0;
	}
	return 0;
}


發佈了406 篇原創文章 · 獲贊 16 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章