poj 1741 + Spoj 1825

噗 ~突然又感觉好久没有更新的样子是什么情况!因为这段时间已经热身差不多啦~所以慢慢的不再写LightOj的题目,虽然LightOj是一个挺好的Oj了啦,但是自己倒是希望写一些更有质量的东西,因此估计又要重操旧业开始写bzoj了~嘤!

然后的话 是按照AC人数来进行的~第一题却是树链剖分Q+Q,应该说是很经典的东西了,然而当初信誓旦旦的说要掌握要掌握~结果却是什么题目都没写!这次的话翻出了2009年国家集训队的论文集,认真的看完写过才是正途!


Tree
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 13806 Accepted: 4457

Description

Give a tree with n vertices,each edge has a length(positive integer less than 1001). 
Define dist(u,v)=The min distance between node u and v. 
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k. 
Write a program that will count how many pairs which are valid for a given tree. 

Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l. 
The last test case is followed by two zeros. 

Output

For each test case output the answer on a single line.

Sample Input

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

Sample Output

8

Source


题意:就是给一棵树,以及每条树边上的权值,询问权值总和小于K的路径有多少条。


Tips:题目是非常经典的点分治题。使用寻找重心+统计+递归可以很好的解决,因此这里只提需要注意的3点。

        1.统计的时候需要比较注意时间复杂度,因为这里统计经过root并且权值总和<=K的路径部分如果超过线性复杂度应该就会T,因此比较好的解决方式是:

            1.1 令 sum=所有经过root权值总和<=K的"点对" (root不为路径端点)   

            

    1.2 sumi=经过子节点i权值总和<=K的点对 (子节点i不为路径端点) 

    

    1.3 det=以root为某一路径端点权值总和<=K的点对 

            

          所有经过root权值总和<=K的路径数为 sum+det-Σsumi

        2.在进行点分治求重心的时候,需要注意: maxn=max(maxn,siz-sonum[now])这个部分的siz是某个块的节点总数,第一次写的时候没有注意直接用全体节点数n来计算,结果导致最重要的提高效率的地方写错。


        3.在所有dfs过程中判断某个节点是否可行的条件仅仅使用edge[i].vi!=fah 是完全不够的,因为在新的分治区域中的root与传递下来的edge[i].vi已经有很大可能不相同了,因此需要有数组对已经进行过统计的root进行隔断,因为是树结构因此只要对点进行隔断就相当于将图分成两块。

        

 


#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
struct node{
	int nex,vi,val;
};
node edge[20005];
int head[10005];
int cnt;

int sonum[10005];
int dist[10005];
bool vised[10005];
int dist_cnt;
int ans;
int cnt_1;
void edge_init(){
	cnt=0;
	memset(head,0,sizeof(head));
	memset(vised,0,sizeof(vised));
}
void addedge(int ui,int vi,int val){
	cnt++;edge[cnt].vi=vi;edge[cnt].val=val;edge[cnt].nex=head[ui];head[ui]=cnt;
}
void find_root(int fah,int now,int &root,int &sum,int siz){
	cnt_1++;
	int maxn=-1;
	sonum[now]=0;
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false){
			find_root(now,edge[i].vi,root,sum,siz);
			sonum[now]+=sonum[edge[i].vi];
			maxn=max(maxn,sonum[edge[i].vi]);
		}
		sonum[now]++;
		maxn=max(maxn,siz-sonum[now]);
		if (root==-1 || maxn<sum){
			root=now;
			sum=maxn;
		}
}
void data_input(){
	edge_init();
	for (int i=0;i<n-1;i++){
		int tmpu,tmpv,tmpval;
		scanf("%d%d%d",&tmpu,&tmpv,&tmpval);
		addedge(tmpu,tmpv,tmpval);
		addedge(tmpv,tmpu,tmpval);
	}
}
void find_dist(int fah,int now,int disnow){
	dist[dist_cnt++]=disnow;
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false)
			find_dist(now,edge[i].vi,disnow+edge[i].val);
}
int cal_pair(int li,int ri){
	int ret=0;
	int c_li=li,c_ri=ri;
	while (c_li<c_ri){
		while (c_li<c_ri && dist[c_li]+dist[c_ri]>k)
			c_ri--;
		ret+=(c_ri-c_li);
		c_li++;
	}
	return (ret);
}
void find_size(int fah,int now,int &siz){
	siz++;
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false)
			find_size(now,edge[i].vi,siz);
}
void gao(int fah,int now){
	//printf("fah: %d --- now: %d\n",fah,now);
	memset(sonum,0,sizeof(sonum));
	int root,sum;
	root=-1;sum=0;
	int siz=0;
	find_size(fah,now,siz);
	find_root(fah,now,root,sum,siz);
	//printf("root :%d\n",root);

	dist_cnt=0;
	int decim=0;
	for (int i=head[root];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false){
			int nowson=edge[i].vi;
			int st=dist_cnt;
			find_dist(root,nowson,edge[i].val);
			sort(dist+st,dist+dist_cnt);
			decim+=cal_pair(st,dist_cnt-1);
		}
		sort(dist,dist+dist_cnt);
		for (int i=0;i<dist_cnt;i++)
			if (dist[i]<=k)
				ans++;
		ans+=(cal_pair(0,dist_cnt-1)-decim);
		vised[root]=true;
		for (int i=head[root];i!=0;i=edge[i].nex)
			if (edge[i].vi!=fah && vised[edge[i].vi]==false)
				gao(root,edge[i].vi);
}
int main(){
	/*cnt_1=0;
	freopen("test.in","r",stdin);*/
	while (scanf("%d%d",&n,&k) && n+k){
		data_input();
		ans=0;
		gao(-1,1);
		printf("%d\n",ans);
	}
}

这个code感觉效率还是有一点点问题,应该是有某些地方写坏了~不过还是AC了2333




FTOUR2 - Free tour II

no tags 

After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, Travel Agent SPOJ goes on with another discount tour.

The tour will be held on ICPC island, a miraculous one on the Pacific Ocean. We list N places (indexed from 1 to N) where the visitors can have a trip. Each road connecting them has an interest value, and this value can be negative (if there is nothing interesting to view there). Simply, these N places along with the roads connecting them form a tree structure. We will choose two places as the departure and destination of the tour.

Since September is the festival season of local inhabitants, some places are extremely crowded (we call them crowded places). Therefore, the organizer of the excursion hopes the tour will visit at most K crowded places (too tiring to visit many of them) and of course, the total number of interesting value should be maximum.

Briefly, you are given a map of N places, an integer K, and M id numbers of crowded place. Please help us to find the optimal tour. Note that we can visit each place only once (or our customers easily feel bored), also the departure and destination places don't need to be different.

Input

There is exactly one case. First one line, containing 3 integers N K M, with 1 <= N <= 200000, 0 <= K <= M, 0 <= M <= N.

Next M lines, each line includes an id number of a crowded place.

The last (N - 1) lines describe (N - 1) two-way roads connected N places, form a b i, with a, b is the id of 2 places, and i is its interest value (-10000 <= i <= 10000).

Output

Only one number, the maximum total interest value we can obtain.

Example

Input:
8 2 3
3
5
7
1 3 1
2 3 10
3 4 -2
4 5 -1
5 7 6
5 6 5
4 8 3


Output:
12

Explanation

We choose 2 and 6 as the departure and destination place, so the tour will be 2 -> 3 -> 4 -> 5 -> 6, total interest value = 10 + (-2) + (-1) + 5 = 12

然后就是spoj的1825,这个题目和上题区别并不是很大,需要注意的还是统计那一部分的内容,对于每一次分治的当前root采用disnow[p]来记录拥挤点数<=p的最大路径值。在这里有一点需要非常注意:

1.路径值为0,路径值非法,这是两种种不同的状态一定要记得区分。以及合并的时候,一个子节点dfs路径过后全部统计结束才对disval进行更新。


2.因为如果是链状的话dfs递归层数将会非常多,因此在dfs过程中尽量不要开局部变量否则会因为递归层数太多导致局部空间无法存储下进而出现segsev

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF = 0x7fffffff;
int n,m,k;
int ans;
struct node{
	int nex,vi,val;
};
node edge[400005];
int cnt;
int head[200005];

int crowd[200005];
bool vised[200005];

int disval[200005];
int disnow[200005];
int so_num[200005];
int so_max[200005];
int root,sum;

struct flo{
	int s_cro,s_id,s_dis;
};
flo dep[200005];
int cmp(flo a,flo b){
	if (a.s_cro!=b.s_cro)
		return (a.s_cro<b.s_cro);
	return (a.s_id<b.s_id);
}
int de_cnt;

void init(){
	cnt=0;
	memset(head,0,sizeof(head));
	memset(crowd,0,sizeof(crowd));
	memset(vised,0,sizeof(vised));
}
void addedge(int ui,int vi,int val){
	cnt++;edge[cnt].vi=vi;edge[cnt].val=val;edge[cnt].nex=head[ui];head[ui]=cnt;
}

void find_size(int fah,int now,int &siz){
	siz++;
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false)
			find_size(now,edge[i].vi,siz);
}
void find_root(int fah,int now,int siz){
	//int ret=0,maxn=-1;
	so_num[now]=0;
	so_max[now]=-1;
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false){
			find_root(now,edge[i].vi,siz);
			so_max[now]=max(so_max[now],so_num[edge[i].vi]);
			so_num[now]+=so_num[edge[i].vi];
		}
		so_num[now]++;
		so_max[now]=max(so_max[now],siz-so_num[now]);
		if (root==-1 || so_max[now]<sum){
			root=now;
			sum=so_max[now];
		}
}
void find_node(int fah,int now,int &cro){
	cro+=crowd[now];
	dep[de_cnt].s_cro=max(dep[de_cnt].s_cro,cro);
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false)
			find_node(now,edge[i].vi,cro);
	cro-=crowd[now];
}
void find_dis(int fah,int now,int cro,int val){
	disnow[cro+crowd[now]]=max(disnow[cro+crowd[now]],val);
	for (int i=head[now];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false)
			find_dis(now,edge[i].vi,cro+crowd[now],val+edge[i].val);
}
void tree_dc(int fah,int now){
	root=-1;
	sum=0;
	de_cnt=0;
	int siz=0;
	find_size(fah,now,siz);
	find_root(fah,now,siz);
	for (int i=head[root];i!=0;i=edge[i].nex)
		if (edge[i].vi!=fah && vised[edge[i].vi]==false){
			dep[de_cnt].s_id=edge[i].vi;
			dep[de_cnt].s_dis=edge[i].val;
			dep[de_cnt].s_cro=-1;
			int n_cro=crowd[root],n_siz=0;
			find_node(root,edge[i].vi,n_cro);
			de_cnt++;
		}
		sort(dep,dep+de_cnt,cmp);

		for (int i=0;i<crowd[root];i++)
			disval[i]=-INF;
		disval[crowd[root]]=0;
		if (de_cnt==0)
			return;
		for (int i=crowd[root]+1;i<=dep[de_cnt-1].s_cro;i++)
			disval[i]=max(disval[i-1],-INF);
		for (int i=0;i<de_cnt;i++){
			for (int j=0;j<=dep[i].s_cro;j++)
				disnow[j]=-INF;
			find_dis(root,dep[i].s_id,crowd[root],dep[i].s_dis);
			for (int j=0;j<=dep[i].s_cro;j++){
				int c_id;
				if (i==0)
					c_id=min(k-j+crowd[root],crowd[root]);
				else
					c_id=min(k-j+crowd[root],dep[i-1].s_cro);
				if (c_id<0 || disnow[j]==-INF || disval[c_id]==-INF)
					continue;
				ans=max(ans,disnow[j]+disval[c_id]);
			}
			for (int j=0;j<=dep[i].s_cro;j++){
				disval[j]=max(disval[j],disnow[j]);
				if (j>0)
					disval[j]=max(disval[j],disval[j-1]);
			}
		}

		vised[root]=true;
		for (int i=head[root];i!=0;i=edge[i].nex)
			if (edge[i].vi!=fah && vised[edge[i].vi]==false)
				tree_dc(root,edge[i].vi);
}
int main(){
	//freopen("test.in","r",stdin);
	scanf("%d%d%d",&n,&k,&m);
	init();
	for (int i=0;i<m;i++){
		int tmp=0;
		scanf("%d",&tmp);
		crowd[tmp]=1;
	}
	for (int i=0;i<n-1;i++){
		int unow,vnow,valnow;
		scanf("%d%d%d",&unow,&vnow,&valnow);

		addedge(unow,vnow,valnow);
		addedge(vnow,unow,valnow);
	}
	//cout<<cnt<<endl;
	ans=0;
	tree_dc(-1,1);
	printf("%d\n",ans);
}



因为现在时间有点晚先贴个代码~回头将再把细节补上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值