Codeforces #302 div1
2015-05-09 14:51:38
总结:... 状态不佳(弱!) 打的很糟糕。
A题,明显的dp,被我搞的很烦,最后在队友提醒下才过掉的...
B题,敲残被hack掉辣
赛后补掉了D题。
A题:dp,背包
用 dp[i][j][k] 来表示用前 i 个人写前 j 行code,总共产生了 k 个 bug(s)
转移的方程很明显:dp[i][j][k] += dp[i-1][j-1][k-a[i]],然后可以降维,开成 dp[j][k] 就够了。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; int n,m,b; ll mod; ll dp[505][505]; int a[505]; int main(){ scanf("%d%d%d%I64d",&n,&m,&b,&mod); for(int i = 1; i <= n; ++i) scanf("%d",&a[i]); dp[0][0] = 1; for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ for(int k = a[i]; k <= b; ++k){ dp[j][k] = (dp[j][k] + dp[j-1][k-a[i]]) % mod; } } } ll ans = 0; for(int i = 0; i <= b; ++i) ans = (ans + dp[m][i]) % mod; printf("%I64d\n",ans); return 0; }
B题:最短路
先跑n遍spfa来求出两两点间的最短距离,然后考虑,s1->t1 , s2->t2 的路径有两种情况,要么相互独立,要么有相交的部分,
而且如果相交,那么相交部分定只有一段。所以枚举一下相交部分的起点和终点就可以了。
hack点:如果枚举的两个点是 i,j ,对于s1需要考虑 s1->i->j->t1 , s1->j->i->t1 两种情况。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; ll dis[3010][3010]; int n,m; vector<int> g[3010]; int inq[3010]; int Spfa(int st){ for(int i = 1; i <= n; ++i) dis[st][i] = INF; dis[st][st] = 0; memset(inq,0,sizeof(inq)); queue<int> Q; while(!Q.empty()) Q.pop(); Q.push(st); while(!Q.empty()){ int x = Q.front(); Q.pop(); for(int i = 0; i < g[x].size(); ++i){ int v = g[x][i]; if(dis[st][v] > dis[st][x] + 1){ dis[st][v] = dis[st][x] + 1; if(inq[v] == 0){ inq[v] = 1; Q.push(v); } } } } } int main(){ int a,b; int s1,t1,l1,s2,t2,l2; scanf("%d%d",&n,&m); for(int i = 1; i <= m; ++i){ scanf("%d%d",&a,&b); g[a].PB(b); g[b].PB(a); } scanf("%d%d%d",&s1,&t1,&l1); scanf("%d%d%d",&s2,&t2,&l2); for(int i = 1; i <= n; ++i) Spfa(i); int ans = (dis[s1][t1] <= l1 && dis[s2][t2] <= l2) ? m - dis[s1][t1] - dis[s2][t2] : -1; for(int i = 1; i <= n; ++i){ for(int j = 1; j <= n; ++j){ ll sp1 = min(dis[j][s1] + dis[i][j] + dis[i][t1], dis[i][s1] + dis[i][j] + dis[j][t1]); ll sp2 = min(dis[j][s2] + dis[i][j] + dis[i][t2], dis[i][s2] + dis[i][j] + dis[j][t2]); if(sp1 <= l1 && sp2 <= l2) ans = max(ans,m - (int)sp1 - (int)sp2 + (int)dis[i][j]); } } printf("%d\n",ans); return 0; }
D题:树形dp
补了挺久... 首先 dp[i] 表示以 i 为根的子树的答案,转移的方程比较明显: (j 为 i 的子节点)
(因为对于 i 的一个子树,如果 i 和 j 的边是 bad 边,那么以 j 为根的子树里的边都要是好边,方案数为1,所以+1)
但是这样子算完只能算一半,因为以 i 为根的子树的上方没有被计算到。用 dp2[i] 来表示整棵树除了以 i 为根的子树的答案。
现在用 pref[u][v] 和 suf[u][v] 来分别表示当前根为 u,编号在 v 之前的答案之积和编号在 v 之后的答案之积。
有公式: ,
那么就有 dp2[u] 的公式:
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 200010; const ll mod = 1e9 + 7; int n; vector<int> g[MAXN]; ll dp[MAXN],dp2[MAXN]; vector<ll> suf[MAXN],pref[MAXN]; void Dfs(int p,int pre){ // dp[i] = π (dp[j] + 1) dp[p] = 1; for(int i = 0; i < g[p].size(); ++i){ int v = g[p][i]; if(v == pre) continue; Dfs(v,p); dp[p] = dp[p] * (dp[v] + 1) % mod; } ll curp = 1,curs = 1; for(int i = 0; i < g[p].size(); ++i){ int v = g[p][i]; if(v == pre) continue; pref[p].PB(curp); curp = curp * (dp[v] + 1) % mod; } for(int i = g[p].size() - 1; i >= 0; --i){ int v = g[p][i]; if(v == pre) continue; suf[p].PB(curs); curs = curs * (dp[v] + 1) % mod; } reverse(suf[p].begin(),suf[p].end()); } void Dfs2(int p,int pre){ for(int i = 0; i < g[p].size(); ++i){ int v = g[p][i]; if(v == pre) continue; dp2[v] = (pref[p][i] * suf[p][i] % mod * dp2[p] % mod + 1) % mod; Dfs2(v,p); } } int main(){ int a; scanf("%d",&n); for(int i = 1; i < n; ++i){ scanf("%d",&a); g[a].PB(i + 1); } Dfs(1,0); for(int i = 0; i <= n; ++i) dp2[i] = 1; Dfs2(1,0); for(int i = 1; i <= n; ++i) printf("%I64d%c",dp[i] * dp2[i] % mod,i == n ? '\n' : ' '); return 0; }