杭二联考8.23
首先我必须吐槽
T1 原题 COGS2095
T2 原题 COGS2096
T3 原题 COGS2097
上面并没什么,然而三题全连号发现了吗?原因竟然是是
不平凡的世界
所以今天就没设密码管他的。
T1
我考试按照边来考虑,想了很久。其实这题按点考虑,最后再考虑边,就很好码了。
首先计算出每个点被烧到的时间。方法:
- DFS。(%%%YYB)
- 我用的树形DP(其实也不算?)先自下往上算出时间,然后自上往下更新。即:第一遍\(f[father] <- f[son] + len\) 第二遍:\(f[son] <- min(f[son],f[father]+len)\) 为什么对呢?因为烧的话只会深度先减后增(或者不增)
然后枚举边算出烧完的时间更新答案。
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cmath>
#define Fname "firelead"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
typedef long long ll;
il int gi(){
rg int x=0,f=1;rg char ch=getchar();
while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=1e5+10,maxm=maxn<<1;
int e[maxn],f[maxn],fir[maxn],nxt[maxm],w[maxm],dis[maxm],id=1,n;
il vd add(int a,int b,int c){nxt[++id]=fir[a],fir[a]=id,w[id]=c,dis[id]=b;}
int d[maxn],ans=0;
il vd dfs(int now,int fa=-1){
if(d[now]==1){f[now]=0;return;}
f[now]=2e9;
erep(i,now)if(dis[i]^fa){
e[dis[i]]=i,dfs(dis[i],now);
f[now]=min(f[now],f[dis[i]]+w[i]);
}
}
il vd son(int now,int fa=-1){
erep(i,now)if(dis[i]^fa)f[dis[i]]=min(f[dis[i]],f[now]+w[i]),son(dis[i],now);
}
int main(){
freopen(Fname".in","r",stdin);
freopen(Fname".out","w",stdout);
n=gi()+1;int a,b,c;
if(n==2){
a=gi(),b=gi(),c=gi();
printf("%.1f\n",c/2.00);
return 0;
}
rep(i,2,n)a=gi(),b=gi(),c=gi()<<1,++d[a],++d[b],add(a,b,c),add(b,a,c);
int root=1;
drep(i,n,1)if(d[i]>1)root=i;
e[root]=0;
dfs(root);
son(root);
rep(i,1,n)if(i!=root){
int u=i,v=dis[e[i]^1],len=w[e[i]];
a=f[u],b=f[v];
if(a>b)swap(a,b);
ans=max(ans,b+((len+a-b)>>1));
}
printf("%.1f\n",ans/2.00);
return 0;
}
T2
正解?以前学长好像讲过。这里只说\(O(n^2)\)算法。
三个点在树上,肯定有且只有一个点,在它们互相之间的路径上,且和这三个点距离都相等。于是先枚举这个中间点
再进行深搜,那么答案就是
\[ \sum_{d为中间点}\sum_{i=1}^{s-2}\sum_{j=i+1}^{s-1}\sum_{k=j+1}^{s}\sum_{dep=1}^{\infty}sum[i][dep]sum[j][dep]sum[k][dep] \]
上面
s表示d的儿子个数
sum[a][b]表示子树a中深度为b的点数。
dep表示你枚举的深度。
dep肯定有枚举限制的,但不好说明于是写个无限。。。
这样会过不去
加一个前缀和优化就能过
记
\[ q[a][b]=\sum_{i=1}^{a}s[i][b] \]
于是不用枚举i了
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<vector>
#define Fname "hopetree"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
typedef long long ll;
il int gi(){
rg int x=0,f=1;rg char ch=getchar();
while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=5010,maxm=maxn<<1;
int n,fir[maxn],nxt[maxm],dis[maxm],id;
il vd add(int a,int b){nxt[++id]=fir[a],fir[a]=id,dis[id]=b;}
int ans=0;
int s[maxn][maxn],q[maxn][maxn],d[maxn];
il vd dfs(int now,int kkk,int dep,int fa=-1){
++s[kkk][dep];
d[kkk]=max(d[kkk],dep);
erep(i,now)if(dis[i]^fa)dfs(dis[i],kkk,dep+1,now);
}
int main(){
// freopen(Fname".in","r",stdin);
// freopen(Fname".out","w",stdout);
n=gi();
int u,v;
rep(i,2,n)u=gi(),v=gi(),add(u,v),add(v,u);
rep(i,1,n){
int Id=0;
erep(j,i){
++Id;
rep(k,1,n)s[Id][k]=0;
d[Id]=0,dfs(dis[j],Id,1,i);
}
rep(j,1,Id)rep(k,1,n)q[j][k]=q[j-1][k]+s[j][k];
rep(b,2,Id)rep(c,b+1,Id)drep(j,min(d[b],d[c]),1)
ans=(ans+q[b-1][j]*s[b][j]*s[c][j])%338;
}
printf("%d %d\n",ans%338+1,(ans+233)%338+1);
return 0;
}