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;
}
View Code

 

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;
}
View Code

 

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;
}
View Code

 

posted @ 2015-05-09 15:10  Naturain  阅读(112)  评论(0编辑  收藏  举报