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 }
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=1∑Kj=1dis(vi,vj)最小.
考虑每条边的贡献,一条边会把树分成两部分,若在其中一部分里选择了x个点,则这条边被统计的次数为x*(K-x)*2.
那么考虑dp[u][i]表示在u的子树中选择了i个点的最小代价,有转移dp[u][i]=minKj=0(dp[u][i−j]+dp[v][j]+j∗(K−j)∗2∗wu,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 }