BZOJ NOI十连测 第一测 T2
Posted GFY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ NOI十连测 第一测 T2相关的知识,希望对你有一定的参考价值。
思路:看到这题,就感觉是一道很熟悉的题目:
http://www.cnblogs.com/qzqzgfy/p/5535821.html
只不过这题的K最多可以到N,而且边权不再只是1,考试的时候yy了一下做法:
找k次直径,第一次把边取反,要是第二次再取到同样的边,那就把它变成0,毕竟每条边只经过2次嘛,YY的很好,实际上,交上去,5分TAT
后来听以为神犇说不是取0,而是继续取反,每条边取一次就取反一次,woc..
PS还有一点:一开始我是准备找出里面一点,然后bfs找最远和次远的点,然后把路径取反的,后面想想太SB了,毕竟这两个点是有可能有相交路径的。。
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define ll long long 7 int dis[200005],val[200005],mxdis,d[200005],p; 8 int tot,go[200005],next[200005],first[200005],n,k,C,cnt=0,ans1,ans2,id[200005]; 9 int pre[200005],edge[200005],op[200005],S,T,vis[200005],c[200005]; 10 int read(){ 11 int t=0,f=1;char ch=getchar(); 12 while (ch<\'0\'||ch>\'9\'){if (ch==\'-\') f=-1;ch=getchar();} 13 while (\'0\'<=ch&&ch<=\'9\'){t=t*10+ch-\'0\';ch=getchar();} 14 return t*f; 15 } 16 void insert(int x,int y,int z){ 17 tot++; 18 go[tot]=y; 19 next[tot]=first[x]; 20 first[x]=tot; 21 val[tot]=z; 22 } 23 void add(int x,int y,int z){ 24 insert(x,y,z);op[tot]=tot+1;insert(y,x,z);op[tot]=tot-1; 25 } 26 void dfs1(int x,int fa){ 27 d[x]=0;ll mx1=0,mx2=0; 28 for (int i=first[x];i;i=next[i]){ 29 int pur=go[i]; 30 if (pur==fa) continue; 31 dfs1(pur,x); 32 if (d[pur]+val[i]>mx1) mx2=mx1,mx1=d[pur]+val[i]; 33 else if (d[pur]+val[i]>mx2) mx2=d[pur]+val[i]; 34 } 35 if (mxdis<mx1+mx2) mxdis=mx1+mx2,S=x; 36 } 37 void clear(int x){ 38 for (int i=x;i!=S;i=pre[i]){ 39 val[edge[i]]=-val[edge[i]];val[op[edge[i]]]=-val[op[edge[i]]]; 40 } 41 } 42 int bfs(int x){ 43 int h=1,t=1; 44 ll mx=0;int Id=x; 45 for (int i=0;i<=n;i++) dis[i]=vis[i]=pre[i]=edge[i]=c[i]=0; 46 c[1]=x; 47 vis[x]=1; 48 while (h<=t){ 49 int now=c[h++]; 50 for (int i=first[now];i;i=next[i]){ 51 int pur=go[i]; 52 if (vis[pur]) continue; 53 dis[pur]=dis[now]+val[i]; 54 pre[pur]=now; 55 edge[pur]=i; 56 vis[pur]=1; 57 c[++t]=pur; 58 } 59 } 60 for (int i=1;i<=n;i++) 61 if (mx<dis[i]) mx=dis[i],Id=i; 62 return Id; 63 } 64 void clear(){ 65 T=bfs(S); 66 ll sx=0;int Id=S; 67 for (int i=1;i<=n;i++) 68 if (i!=S&&i!=T&&sx<dis[i]) sx=dis[i],Id=i; 69 clear(Id);clear(T); 70 } 71 void Find_longest(){ 72 mxdis=-0x7fffffff; 73 dfs1(1,0); 74 clear(); 75 } 76 void dfs(int x,int fa){ 77 int mx1=0,mx2=0;d[x]=0;int id1=x,id2=x; 78 for (int i=first[x];i;i=next[i]){ 79 int pur=go[i]; 80 if (pur==fa) continue; 81 dfs(pur,x); 82 if (d[pur]+val[i]>mx1) mx2=mx1,mx1=d[pur]+val[i],id2=id1,id1=id[pur]; 83 else if (d[pur]+val[i]>mx2) mx2=d[pur]+val[i],id2=id[pur]; 84 } 85 id[x]=id1; 86 d[x]=mx1; 87 if (mxdis<mx1+mx2) mxdis=mx1+mx2,ans1=id1,ans2=id2; 88 } 89 void dfss(int x,int y,int fa){ 90 if (!x||!y) return; 91 if (x==y) {p=1;return;} 92 for (int i=first[x];i;i=next[i]){ 93 int pur=go[i]; 94 if (pur==fa) continue; 95 dfss(pur,y,x); 96 if (p) {val[i]=-val[i],val[op[i]]=-val[op[i]];return;} 97 } 98 } 99 int main(){ 100 while (scanf("%d",&n)!=EOF){ 101 k=read();C=read(); 102 tot=0; 103 int sum=0; 104 for (int i=1;i<=n;i++) first[i]=0; 105 for (int i=1;i<n;i++){ 106 int x=read(),y=read(),z=read(); 107 x++;y++; 108 add(x,y,z); 109 sum+=z; 110 } 111 sum*=2; 112 for (int i=1;i<=k;i++){ 113 mxdis=0; 114 dfs(1,0); 115 if (mxdis<C) break; 116 sum=sum-mxdis+C; 117 p=0; 118 dfss(ans1,ans2,0); 119 } 120 printf("%d\\n",sum); 121 } 122 }
然后听说有树形dp做法,由于每条边至多经过2次。假如每次穿越都新加了一条"穿越边",每次穿越都新加了2个"新点",假如子树x内有k个新点,那么(fa[x],x)这条边经过的边奇偶性和k个奇偶性相同,因为要从0出发,再回到0,这是个欧拉回路,因此进入子树p有x条边的话,离开子树p也必须是x条边,因此,奇偶性相同就得证了,由此我们设dp方程
f[x][p]代表x的子树,有p个新节点的最小代价,然后用树上背包做就好了,(虽然这看起来像n^3,但是是n^2.....)
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define inf 0x3f3f3f3f 7 int tot,go[200005],first[200005],next[200005],val[200005]; 8 int son[200005],f[2005][2005],g[2005],n,K,C; 9 int read(){ 10 int t=0,f=1;char ch=getchar(); 11 while (ch<\'0\'||ch>\'9\'){if (ch==\'-\') f=-1;ch=getchar();} 12 while (\'0\'<=ch&&ch<=\'9\'){t=t*10+ch-\'0\';ch=getchar();} 13 return t*f; 14 } 15 void insert(int x,int y,int z){ 16 tot++; 17 go[tot]=y; 18 next[tot]=first[x]; 19 first[x]=tot; 20 val[tot]=z; 21 } 22 void add(int x,int y,int z){ 23 insert(x,y,z);insert(y,x,z); 24 } 25 void dfs(int x,int fa,int y){ 26 son[x]=1; 27 for (int i=1;i<=n;i++) f[x][i]=inf; 28 f[x][0]=0; 29 for (int i=first[x];i;i=next[i]){ 30 int pur=go[i]; 31 if (pur==fa) continue; 32 dfs(pur,x,val[i]); 33 for (int j=0;j<=son[x];j++) g[j]=f[x][j],f[x][j]=inf; 34 for (int j=0;j<son[x];j++) 35 for (int k=0;k<=son[pur];k++) 36 f[x][j+k]=std::min(f[x][j+k],g[j]+f[pur][k]); 37 son[x]+=son[pur]; 38 } 39 for (int i=son[x];i>=1;i--) 40 f[x][i]=std::min(f[x][i],f[x][i-1]); 41 for (int i=0;i<=son[x];i++) f[x][i]+=((i%2==0)+1)*y; 42 } 43 int main(){ 44 while (scanf("%d",&n)!=EOF){ 45 K=read();C=read(); 46 tot=0; 47 for (int i=1;i<=n;i++) 48 first[i]=0; 49 for (int i=1;i<n;i++){ 50 int x=read(),y=read(),z=read(); 51 x++;y++; 52 add(x,y,z); 53 } 54 dfs(1,0,0); 55 int ans=inf; 56 for (int i=0;i<=K&&i*2<=n;i++) 57 ans=std::min(ans,f[1][i*2]+C*i); 58 printf("%d\\n",ans); 59 } 60 }
以上是关于BZOJ NOI十连测 第一测 T2的主要内容,如果未能解决你的问题,请参考以下文章