CodeForces #179(295A|295B|295C)|动态规划|最短路径|前缀和

295A - Greg and Array

题目大意

对于已经给出初始值的数组a[n],m个操作是给区间[l,r]的所有元素+d。你又有k个询问,执行[x,y]的操作。求k个询问后的数组。

题解

显然,m个操作间没有顺序,故我们只需要统计各个操作被执行了多少次,通过前缀和简单实现。然后同样用前缀和处理m个操作。

#include <cstdio>
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int N = 100005, M = N;
typedef long long ll;
int l[M], r[M], p[M], n;
ll c[N], d[M];
void add(int i, ll x) {
    for (; i <= n; i += i & -i) c[i] += x;
}
ll sum(int i) {
    ll s = 0;
    for (; i; i -= i & -i) s += c[i];
    return s;
}
int main() {
    int a, m, k, i, x, y, q = 0;
    scanf("%d%d%d", &n, &m, &k);
    FOR(i,1,n) scanf("%I64d", &a), c[i] += a, c[i + 1] -= a;
    FOR(i,1,m) scanf("%d%d%d", l + i, r + i, d + i);
    FOR(i,1,k) {
        scanf("%d%d", &x, &y);
        ++p[x]; --p[y + 1];
    }
    FOR(i,1,m) {
        q += p[i]; c[l[i]] += d[i] * q; c[r[i] + 1] -= d[i] * q;
    }
    FOR(i,1,n) printf("%I64d ", c[i]);
    return 0;
}

295B - Greg and Graph

题目大意

给出n个点的有向完全图,求每删一个点图中各点对间最短路径长度之和。

题解

只有删除操作的题目一般都离线转为添加操作
如果是添加的话,每添加一个点跑一次多源最短路径显然Floyd。

#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
typedef long long ll;
const int N = 505;
ll dis[N][N], ans[N], d[N];
bool vis[N];
int main() {
    int i, j, k, l, n;
    scanf("%d", &n);
    FOR(i,1,n) FOR(j,1,n) scanf("%I64d", &dis[i][j]), ans[0] += dis[i][j];
    FOR(i,1,n) scanf("%d", &d[i]);
    for(l=n;l;--l) {
        k = d[l]; vis[k] = 1;
        FOR(i,1,n) FOR(j,1,n) {
            dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
            if (vis[i] && vis[j]) ans[l] += dis[i][j];
        }
    }
    FOR(i,1,n) printf("%I64d ", ans[i]);
    return 0;
}

295C - Greg and Friends

#include <cstdio>
#include <queue>
#define FOR(i,j,k) for(i=j;i<=k;++i)
typedef long long ll;
const int N = 55, MOD = 1000000007;
using namespace std;
struct Data { int n1, n2, type;
    int id() { return n1 * N * N + n2 * N + type; }
};
ll dp[N * N * 2], path[N * N * 2], C[N][N];
queue<Data> q;
int main() {  
    int i, j, x, n1 = 0, n2 = 0, n, k;
    Data h, p;
    FOR(i,0,50) C[i][0] = 1;
    FOR(i,1,50) FOR(j,1,50)
        C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
    FOR(i,1,n) {
        scanf("%d",&x);
        if (x == 50) n1++; else n2++;
    }
    h = (Data){n1, n2, 0};
    dp[h.id()] = path[h.id()] = 1;
    q.push(h);
    while (!q.empty()) {
        u = q.front(); q.pop();
        for(j=0;j<=u.n2&&j*100<=k;++j) {
            for(i=0;i<=u.n1&&i*50+j*100<=k;++i) if (j || i) {
                 v = (Data) {n1 - u.n1 + i, n2 - u.n2 + j, 1 - u.type};
                 if (!dp[v.id()]) {
                      dp[v.id()] = dp[u.id()] + 1;
                      q.push(v);
                 }
                 if (dp[v.id()] == dp[u.id()] + 1) {
                      ll t = (C[u.n1][i] * C[u.n2][j]) % MOD;
                      (t *= path[u.id()]) %= MOD;
                      (path[v.id()] += t) %= MOD;
                 }
            }
        }
    }
    printf("%I64d\n%I64d", dp[((Data){n1,n2,1}).id()]-1, path[((Data){n1,n2,1}).id()]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值