1. 程式人生 > >【10.31校內測試】【組合數學】【記憶化搜尋/DP】【多起點多終點二進位制拆位Spfa】

【10.31校內測試】【組合數學】【記憶化搜尋/DP】【多起點多終點二進位制拆位Spfa】

Solution

 

注意取模!!!

Code

#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long
using namespace std;

int n, a, b;

LL mpow(LL a, LL b) {
    LL ans = 1;
    for(; b; b >>= 1, a = a * a % mod)
        if(b & 1)    ans = ans * a % mod;
    return ans;
}

LL fac[
200005], inv[200005], l[100005], t[100005]; void init() { fac[0] = 1; inv[0] = 1; for(int i = 1; i <= 200000; i ++) { fac[i] = fac[i - 1] * i % mod; inv[i] = mpow(fac[i], mod - 2); } } LL C(int p, int q) { return fac[p] * inv[q] % mod * inv[p - q] % mod; } int main() { freopen(
"matrix.in", "r", stdin); freopen("matrix.out", "w", stdout); scanf("%d%d%d", &n, &b, &a); for(int i = 1; i <= n; i ++) scanf("%lld", &l[i]); for(int i = 1; i <= n; i ++) scanf("%lld", &t[i]); LL ans = 0; init(); for(int i = 2; i <= n; i ++) { ans
= (ans + b * l[i] % mod * C(n - 2 + n - i, n - 2) % mod * mpow(a, n - i) % mod * mpow(b, n - 2) % mod) % mod; ans = (ans + a * t[i] % mod * C(n - 2 + n - i, n - 2) % mod * mpow(b, n - i) % mod * mpow(a, n - 2) % mod) % mod; } printf("%lld", ans); return 0; }

Solution

二分+DP,二分需要多少組p+q,記憶化搜尋判斷是否可以達到條件。

定義$dp[dep][j][k]$表示當前取到第$dep$個數,還需要j個p,k個q來使滿足條件。(p>q)

每次先儘量放p,剩下中再儘量放q,放q時就有限制,不能取超過$mid-i$,i表示當前取的p的數量。

Code

#include<bits/stdc++.h>
using namespace std;

int n, a[55], p, q;
int sum[55], vis[55][1005][1005], tim, all;
bool dfs(int dep, int nump, int numq) {
    if(nump == 0 && numq == 0)    return 1;
    if(dep == n + 1 || sum[dep] < nump * p + numq * q)    return 0;
    if(vis[dep][nump][numq] == tim)    return 0;
    for(int i = 0; i <= min(nump, a[dep] / p); i ++)
        if(dfs(dep + 1, nump - i, max(numq - min((a[dep] - p * i) / q, all - i), 0)))    return 1;
    vis[dep][nump][numq] = tim;
    return 0;
}

bool check(int mid) {
    tim ++;
    all = mid;
    return dfs(1, mid, mid);
}

int erfen() {
    int l = 0, r = sum[1] / (p + q), ans;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(check(mid))    ans = mid, l = mid + 1;
        else    r = mid - 1;
    }
    return ans;
}

int main() {
    freopen("pq.in", "r", stdin);
    freopen("pq.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)    scanf("%d", &a[i]);
    for(int i = n; i >= 1; i --)    sum[i] = sum[i + 1] + a[i];
    scanf("%d%d", &p, &q);
    if(p < q)    swap(p, q);
    int ans = erfen();
    printf("%d", ans * (p + q));
    return 0;
}

 

Solution

考試最後半小時開始寫QAQ結果發現是做過的題啊??

可是最後Spfa寫錯了!!!我爆哭!!!!

將與1相連的所有點取出來,二進位制分類,分成起點和終點,跑多起點多終點的Spfa,處理出兩兩起點終點間最短路,更新答案即可QAQ

Spfa要判更新的點不能是1!!

Code

#include<bits/stdc++.h>
using namespace std;

int n, m;

struct Node {
    int v, nex, w;
} Edge[200005];

int h[30005], stot = 1;
void add(int u, int v, int w) {
    Edge[++stot] = (Node) {v, h[u], w};
    h[u] = stot;
}

int dis[30005];
int vis[30005], nums, numt, w[30005], t[30005], S[30005], T[30005];
void Spfa1() {
    queue < int > q;
    memset(vis, 0, sizeof(vis));
    memset(dis, 0x3f3f3f3f, sizeof(dis));
    for(int i = 1; i <= nums; i ++)    q.push(S[i]), vis[S[i]] = 1, dis[S[i]] = min(dis[S[i]], w[S[i]]);
    while(!q.empty()) {
        int u = q.front();    q.pop();    vis[u] = 0;
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(dis[v] > dis[u] + Edge[i].w && v != 1) {
                dis[v] = dis[u] + Edge[i].w;
                if(!vis[v]) {
                    vis[v] = 1;    q.push(v);
                }
            }
        }
    }
}

void Spfa2() {
    queue < int > q;
    memset(vis, 0, sizeof(vis));
    memset(dis, 0x3f3f3f3f, sizeof(dis));
    for(int i = 1; i <= numt; i ++)    q.push(T[i]), vis[T[i]] = 1, dis[T[i]] = min(dis[T[i]], w[T[i]]);
    while(!q.empty()) {
        int u = q.front();    q.pop();    vis[u] = 0;
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(dis[v] > dis[u] + Edge[i].w && v != 1) {
                dis[v] = dis[u] + Edge[i].w;
                if(!vis[v]) {
                    vis[v] = 1;    q.push(v);
                }
            }
        }
    }
}

int tov[30006], tot;
int main() {
    freopen("graph.in", "r", stdin);
    freopen("graph.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i ++) {
        int u, v, a, b;
        scanf("%d%d%d%d", &u, &v, &a, &b);
        add(u, v, a);    add(v, u, b);
    }
    int ans = 0x3f3f3f3f;
    memset(w, 0x3f3f3f3f, sizeof(w));
    memset(t, 0x3f3f3f3f, sizeof(t));
    for(int i = h[1]; i; i = Edge[i].nex)
        tov[++tot] = Edge[i].v, w[Edge[i].v] = min(w[Edge[i].v], Edge[i].w), t[Edge[i].v] = min(t[Edge[i].v], Edge[i ^ 1].w);
    sort(tov + 1, tov + 1 + tot);
    int M = tov[tot], tt = 0;
    while(M) {
        int tmp = (M & 1);
        nums = 0, numt = 0;
        for(int i = 1; i <= tot; i ++)
            if(((tov[i] >> tt) & 1) == tmp)    S[++nums] = tov[i];
            else    T[++numt] = tov[i];
        Spfa1(); 
        for(int j = 1; j <= numt; j ++) {
            ans = min(ans, t[T[j]] + dis[T[j]]);
        }
        Spfa2();
        for(int j = 1; j <= nums; j ++) {
            ans = min(ans, t[S[j]] + dis[S[j]]);
        }
        M >>= 1, tt ++;
    }
    printf("%d", ans);
    return 0;
}