Bestcoder #23

2014-12-20 22:30:18

总结:做了两道...慢成狗,降了一点orz....

A:水题...莫名其妙wa一发。。。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const int INF = 1 << 28;
20 const int maxn = 4010;
21 
22 int T,K,n;
23 int first[maxn],next[maxn],ver[maxn],w[maxn],ecnt;
24 ll dp[maxn][60];
25 ll siz[maxn];
26 
27 void Init(){
28     fill(dp[0],dp[0] + maxn * 60,INF);
29     memset(first,-1,sizeof(first));
30     ecnt = 0;
31 }
32 
33 void Add_edge(int u,int v,int fee){
34     next[++ecnt] = first[u];
35     ver[ecnt] = v;
36     w[ecnt] = fee;
37     first[u] = ecnt;
38 }
39 
40 void Dfs(int p,int fa){
41     siz[p] = 1;
42     dp[p][1] = dp[p][0] = 0;
43     for(int i = first[p]; i != -1; i = next[i]){
44         int v = ver[i];
45         if(v == fa) continue;
46         Dfs(v,p);
47         siz[p] += siz[v];
48         for(int m = min(siz[p],(ll)K); m >= 0; --m){
49             for(int j = 0; j <= m; ++j){
50                 dp[p][m] = min(dp[p][m],dp[p][m - j] + dp[v][j] + (ll)j * (K - j) * w[i]);
51             }
52         }
53     }
54 }
55 
56 int main(){
57     int a,b,c;
58     scanf("%d",&T);
59     while(T--){
60         Init();
61         scanf("%d%d",&n,&K);
62         for(int i = 1; i < n; ++i){
63             scanf("%d%d%d",&a,&b,&c);
64             Add_edge(a,b,c);
65             Add_edge(b,a,c);
66         }
67         Dfs(1,0);
68         printf("%I64d\n",dp[1][K] * 2);
69     }
70     return 0;
71 }
View Code

B:想了好久orz。。。蠢哭了,想到最后就是枚举中间线,然后算左右半边的情况数,乘起来即可。从前往后、从后往前扫一遍,用树状数组记录即可。注意long long

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const int INF = 1 << 30;
20 const int maxn = 50000;
21 
22 int T,n,v[maxn + 10];
23 ll s1[maxn + 10],s2[maxn + 10];
24 
25 struct BIT{
26     int c[maxn + 10];
27     void clear(){
28         memset(c,0,sizeof(c));
29     }
30     int Lowbit(int x){
31         return x & (-x);
32     }
33     int Getsum(int x){
34         int res = 0;
35         while(x){
36             res += c[x];
37             x -= Lowbit(x);
38         }
39         return res;
40     }
41     void Update(int x,int d){
42         while(x <= maxn){
43             c[x] += d;
44             x += Lowbit(x);
45         }
46     }
47 }B1,B2;
48 
49 int main(){
50     scanf("%d",&T);
51     while(T--){
52         B1.clear();
53         B2.clear();
54         memset(s1,0,sizeof(s1));
55         memset(s2,0,sizeof(s2));
56         scanf("%d",&n);
57         for(int i = 1; i <= n; ++i)
58             scanf("%d",v + i);
59         for(int i = 1; i <= n; ++i){
60             s1[i] = B1.Getsum(v[i] - 1);
61             B1.Update(v[i],1);
62         }
63         for(int i = n; i >= 1; --i){
64             s2[i] = s2[i + 1];
65             s2[i] += B2.Getsum(maxn + 1) - B2.Getsum(v[i]);
66             B2.Update(v[i],1);
67         }
68         ll ans = 0;
69         for(int i = 1; i < n; ++i){
70             ans = ans + s1[i] * s2[i + 1];
71         }
72         printf("%I64d\n",ans);
73     }
74     return 0;
75 }

C:树DP,没啥好说,没敲出来。看了题解和别人代码,发现还是挺精简的。引用一下题解。

选出K个点v1,v2,...vK使得Ki=1Kj=1dis(vi,vj)最小.
考虑每条边的贡献,一条边会把树分成两部分,若在其中一部分里选择了x个点,则这条边被统计的次数为x*(K-x)*2.
那么考虑dp[u][i]表示在u的子树中选择了i个点的最小代价,有转移dp[u][i]=minKj=0(dp[u][ij]+dp[v][j]+j(Kj)2wu,v),式子中u为v的父亲,wu,v表示(u,v)这条边的长度. 时间复杂度O(nK^2).

需要注意的是初始化:dp[i][0] = dp[i][1] = 1,因为不取和只取自己都是0
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const ll INF = 100000000000000000LL;
20 const int maxn = 4010;
21 
22 ll T,K,n;
23 ll first[maxn],next[maxn],ver[maxn],ecnt;
24 ll dp[maxn][60],w[maxn];
25 ll siz[maxn];
26 
27 void Init(){
28     fill(dp[0],dp[0] + maxn * 60,INF);
29     memset(first,-1,sizeof(first));
30     ecnt = 0;
31 }
32 
33 void Add_edge(ll u,ll v,ll fee){
34     next[++ecnt] = first[u];
35     ver[ecnt] = v;
36     w[ecnt] = fee;
37     first[u] = ecnt;
38 }
39 
40 void Dfs(ll p,ll fa){
41     siz[p] = 1;
42     dp[p][1] = dp[p][0] = 0;
43     for(ll i = first[p]; i != -1; i = next[i]){
44         ll v = ver[i];
45         if(v == fa) continue;
46         Dfs(v,p);
47         siz[p] += siz[v];
48         for(ll m = min(siz[p],(ll)K); m >= 0; --m){
49             for(ll j = 0; j <= m; ++j){
50                 dp[p][m] = min(dp[p][m],dp[p][m - j] + dp[v][j] + (ll)j * (K - j) * w[i]);
51             }
52         }
53     }
54 }
55 
56 int main(){
57     ll a,b,c;
58     scanf("%I64d",&T);
59     while(T--){
60         Init();
61         scanf("%I64d%I64d",&n,&K);
62         for(ll i = 1; i < n; ++i){
63             scanf("%I64d%I64d%I64d",&a,&b,&c);
64             Add_edge(a,b,c);
65             Add_edge(b,a,c);
66         }
67         Dfs(1,0);
68         printf("%I64d\n",dp[1][K] * 2);
69     }
70     return 0;
71 }
 

转载于:https://www.cnblogs.com/naturepengchen/articles/4176043.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值