毕竟今天是植树节(一周后)考了两道树,一道最小树形图(然而最近太颓了还没有码过朱刘算法【伐开心
明明算法都对了然而写的丑于是悲剧地200‘的题卡成了20‘
第三题数据简直要报警,暴力20’,然而骗分输出-1竟然有30‘
顺便第一题是sdoi2013,然而从bzoj上拷下来标称测试,跑电子坑大的数据竟然每个结果都不一样,结果交bzoj都能A【然而表示我被卡成n^2的程序能A电子坑大的于是交bzoj T得飞起来233
第一题
Description小Q最近学习了一些图论知识。
根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有N-1条边。
路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a, b) 表示点 a 和点 b 的路径上各边长度之和。称 dis(a,b) 为 a、b 两个节点间的距离。
直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。
现在小 Q 想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。
Input
第一行两个整数N
接下来N-1行,每行3个数,表示一条树边的起点和终点和权值
Ouput
第一行,一个数,表示直径的长度。
第二行,一个数,表示有多少条边满足所有的直径都经过该边。
Sample Input
6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100
Sample Output
1110
2
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;
}
第三题
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,冯天才,长智力,编程就像玩游戏