20160305】的考试【补写一点东西

20 篇文章 0 订阅
6 篇文章 0 订阅

毕竟今天是植树节(一周后)考了两道树,一道最小树形图(然而最近太颓了还没有码过朱刘算法【伐开心

明明算法都对了然而写的丑于是悲剧地200‘的题卡成了20‘

第三题数据简直要报警,暴力20’,然而骗分输出-1竟然有30‘

顺便第一题是sdoi2013,然而从bzoj上拷下来标称测试,跑电子坑大的数据竟然每个结果都不一样,结果交bzoj都能A【然而表示我被卡成n^2的程序能A电子坑大的于是交bzoj T得飞起来233


第一题

CodeVS 1952 直径

Description
小Q最近学习了一些图论知识。
根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有N-1条边。
路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a, b) 表示点 a 和点 b 的路径上各边长度之和。称 dis(a,b) 为 a、b 两个节点间的距离。 
直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。
现在小 Q 想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。
Input
第一行两个整数N
接下来N-1行,每行3个数,表示一条树边的起点和终点和权值


Ouput
第一行,一个数,表示直径的长度。
第二行,一个数,表示有多少条边满足所有的直径都经过该边。
Sample Input

3  1 1000
1  4 10
4  2 100
4  5 50
4  6 100
Sample Output
1110

20% N<=10 
60% N<=1000 

100% N<=200000

思路:

1)求直径水题嘛,顺便再标记一下,

2)之后每个点分别bfs,于是理论上是O(n),然而我手贱把memset写在了bfs内部于是开心地卡成了O(n^2)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;	int n;
const int MAXN=200057;

long long ans=0,cnt=0;

struct t1{
	int to,nxt,lth;
}edge[MAXN*2];	int cnt_edge=0;
int fst[MAXN],dpt[MAXN];
long long dis[MAXN];
int fth[MAXN];

void addedge(int x,int y,int z){
	edge[++cnt_edge].to=y;
	edge[cnt_edge].lth=z;
	edge[cnt_edge].nxt=fst[x];
	fst[x]=cnt_edge;
}

int root=1;

int q[MAXN],head,tail;
int bfs(int now){
	memset(dis,0,sizeof(dis));
	memset(fth,0,sizeof(fth));
	int Rec=0;
	head=tail=0;
	q[tail++]=now;
	while(head^tail){
		now=q[head++];
		for(int tmp=fst[now];tmp;tmp=edge[tmp].nxt){
			if(edge[tmp].to==fth[now])	continue;
			fth[edge[tmp].to]=now;
			dis[edge[tmp].to]=dis[now]+edge[tmp].lth;
			dpt[edge[tmp].to]=dpt[now]+1;
			q[tail++]=edge[tmp].to;
			Rec=dis[Rec]>dis[edge[tmp].to]?Rec:edge[tmp].to;
		}
	}
	return Rec;
}

int tag[MAXN];
int Head,Tail;
int fth2[MAXN];
int bfss(int x,int f){
	int rec=0;
	
	head=tail=0;
	q[tail++]=x;
	int now;
	while(head^tail){
		now=q[head++];
		for(int tmp=fst[now];tmp;tmp=edge[tmp].nxt){
			if(tag[edge[tmp].to]||edge[tmp].to==fth2[now])	continue;
			fth2[edge[tmp].to]=now;
			if(dis[edge[tmp].to]-dis[x]==f){
				++rec;
				return 1;
			}
			q[tail++]=edge[tmp].to;		
		}
	}
	return 0;
}


int bg,ed;

void doit(){
	bg=Tail,ed=Head;
	int now=Tail;
	while(now^Head)
		tag[now]=1,now=fth[now];
	for(now=Tail;now^Head;now=fth[now]){
		int bz1=dis[now],bz2=ans-dis[now];
		if(bfss(now,bz1))	ed=dpt[now]>dpt[ed]?now:ed;
		if(bfss(now,bz2))	bg=dpt[now]<dpt[bg]?now:bg;
	}
}

int main(){
	memset(edge,0,sizeof(edge));
	memset(fst,0,sizeof(fst));
	memset(dis,0,sizeof(dis));
	memset(dpt,0,sizeof(dpt));
	memset(tag,0,sizeof(tag));
	memset(fth2,0,sizeof(fth2));
	
	scanf("%d",&n);
	for(int i=1;i<n;++i){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		addedge(a,b,c);
		addedge(b,a,c);
	}
	
	Head=bfs(1);
	Tail=bfs(Head);
	ans=dis[Tail];
	doit();
	cnt=dpt[bg]-dpt[ed];
	cout<<ans<<'\n'<<cnt;
	return 0;
} 



第二题

Description
给出一棵N个点树,以及Q个询问。
每个询问如下,给出树上的三个点,找到树上的一个点,使得这三个点到第四个点的距离和最小
Input
第一行两个整数N,Q
接下来N-1行,每行2个数,表示一条树边的起点和终点
接下来Q行,每行3个树,表示一个询问中的三个点
Ouput
共Q行,每行两个数,第一个表示第四个点的编号,第二个表示三个点到第四个点的距离和
Sample Input
6 4 
1 2 
2 3 
2 4 
4 5 
5 6 
4 5 6 
6 3 1 
2 4 4 
6 6 6 
Sample Output
5 2 
2 5 
4 1 
6 0 
20% N<=100 Q<=100
60% N<=1000 Q<=1000
100% N<=200000 Q<=200000

思路:

1)表示我就一句话,虽然是三个lca,但是,算距离的时候,并不是,深度相减,因为,这是,三个点的,lca, 啊,啊,啊

2)还是写一下好了,三个点两两求lca,然后那个只出现过一次的就是

3)顺手槽一句,学了链剖之后写倍增就再也没对……【说不定哪天就被拉进了tarjan或者欧拉序+ST表的坑了

#include<cstdio>
#include<algorithm>
#include<cstring>

//#define FLAZE_DEBUG
using namespace std;	int n,m;
long long ans=0,cnt=0;

struct t1{
	int to,nxt;
}edge[200057];	int cnt_edge=0;
int fst[200057];
void addedge(int x,int y){
	edge[++cnt_edge].to=y;
	edge[cnt_edge].nxt=fst[x];
	fst[x]=cnt_edge;
}

//============================================================
int fth[200057],top[200057],son[200057],size[200057];
int dpt[200057];

int dfs1(int x){
	size[x]=1;
	int mxx=0;
	for(int tmp=fst[x];tmp;tmp=edge[tmp].nxt){
		if(edge[tmp].to==fth[x])	continue;
		fth[edge[tmp].to]=x;
		dpt[edge[tmp].to]=dpt[x]+1;
		int tmpp=dfs1(edge[tmp].to);
		size[x]+=tmpp;
		if(tmpp>mxx){
			mxx=tmpp;
			son[x]=edge[tmp].to;
		}
	}
	return size[x];
}

void dfs2(int x,int t){
	top[x]=t;
	if(son[x]) dfs2(son[x],t);
	for(int tmp=fst[x];tmp;tmp=edge[tmp].nxt){
		if(edge[tmp].to==son[x])	continue;
		dfs2(edge[tmp].to,edge[tmp].to);
	}
}

int lca(int x,int y){
	while(top[x]!=top[y]){
		int tx=top[x],ty=top[y];
		if(dpt[ty]>dpt[tx])
			y=fth[top[y]];
		else	x=fth[top[x]];
		
	}
	return dpt[x]>dpt[y]?y:x;
}

int dis(int x,int y){
	int t=lca(x,y);
	return dpt[x]+dpt[y]-dpt[t]*2;
}

int main(){
	freopen("gather.in","r",stdin);
	freopen("gather.out","w",stdout);
	
	memset(fst,0,sizeof(fst));
	memset(edge,0,sizeof(edge));
	memset(size,0,sizeof(size));
	memset(son,0,sizeof(son));
	memset(dpt,0,sizeof(dpt));
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;++i){
		int a,b;
		scanf("%d%d",&a,&b);
		addedge(a,b);
	}
	dfs1(1);
	dfs2(1,1);

#ifdef FLAZE_DEBUG	
	for(int i=1;i<=n;++i){
		printf("%d:   fth:%d    dis:%d\n",i,fth[i],dis[i]);
	}
#endif
	while(m--){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		int l1=lca(a,b),l2=lca(a,c),l3=lca(b,c);
		int aim;
		if(l1==l2)	aim=l3;
		if(l2==l3)	aim=l1;
		if(l1==l3)	aim=l2;
		int ans=dis(aim,a)+dis(aim,b)+dis(aim,c);
		printf("%d %d\n",aim,ans);
	}
	return 0;
}




第三题

hdu4966 GGS-DDU

Description
凤神开始了他的新学期学习。
凤神这个学期有N门课,一开始他的每门课等级都是0。接着他拿到了M个提升班的信息,每个班有有a,l1,b,l2,w,表示只有第a门课程在l1及以上时才能选修,花费代价w,可以将第b门课程提升到l2等级。现在就将所有的课程提升到最高等级需要付出的最小代价,若无法达到则输出-1。
Input
第一行两个整数N,M
第二行是每个课程的满级
接下来M行,每行5个数,a,l1,b,l2,w,意义如上
Ouput
一个数,表示最终花费。如无解输出-1
Sample Input
3 4
3 3 1
1 0 2 3 10
2 1 1 2 10
1 2 3 1 10
3 1 1 3 10
Sample Output
40
20% N<=5 M<=20
60% N<=20 M<=40
100% N<=50 M<=2000
答案小于1e9

1)一眼看上去是费用流

2)然后就跪了

3)把每个学科的每个等级建成点,再加一个超级源连在零级的点上

4)每一个等级向前一个等级加边,花费为0;再根据可以学习的课程加上各种边

5)然后就跑dinic啦x ………………好嘛其实是最小树形图啊orzorz  还没有码这道题,今天回家试试orz


顺手%%%左边的FTC,冯天才,长智力,编程就像玩游戏

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值