各种OJ刷题记录6.9-6.15

各种OJ刷题记录6.9-6.15


[模板]多项式求逆

学习了一个多项式求逆元的奥义…

考虑: B(x)C(x)=1(modxn)

移项并平方: B2(x)C2(x)2B(x)C(x)+1=1(modx2n)

移项得到: B(x)(2C(x)B(x)C(x))=1(modx2n)

然后可以由 modx 倍增上来,复杂度 O(nlgn)

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

const int MAXN = 400005;
const int mod = 998244353, g = 3;
int rev[MAXN];

int power(int a, int n)
{
    int ans = 1, b = a;
    for (int i = 0; i <= 30; i++) {
        if ((n>>i)&1) ans = (long long)ans*b%mod;
        b = (long long)b*b%mod;
    }
    return ans;
}

inline int inv(int a)
{ return power(a, mod-2); }

void NTT(int a[], int n, int flag)
{
    int lg = 0;
    for (int i = 1; i < n; i<<=1) lg++;
    rev[0] = 0;
    for (int i = 1; i < n; i++)
        rev[i] = (rev[i>>1]>>1)|((i&1)<<(lg-1));
    for (int i = 0; i < n; i++)
        if (rev[i]<i) swap(a[i], a[rev[i]]);
    for (int k = 1; k <= n; k <<= 1) {
        int dw = power(g, (mod-1)/k);
        if (flag == -1) dw = inv(dw);
        for (int i = 0; i < n; i += k) {
            int w = 1, u, v;
            for (int j = 0; j < k>>1; j++) {
                u = a[i+j], v = (long long)w*a[i+j+(k>>1)]%mod;
                a[i+j] = (u+v)%mod, a[i+j+(k>>1)] = ((u-v)%mod+mod)%mod;
                w = (long long)w*dw%mod;
            }
        }
    }
    if (flag == -1) {
        int iv = inv(n);
        for (int i = 0; i < n; i++)
            a[i] = (long long)a[i]*iv%mod;
    }
}

int tmp[MAXN+MAXN];

void get_inv(int a[], int b[], int n)
{
    if (n == 1) {b[0] = inv(a[0]); return; }
    get_inv(a, b, n>>1);
    memcpy(tmp, a, sizeof(int)*n), memset(tmp+n, 0, sizeof(int)*n);
    NTT(tmp, n<<1, 1), NTT(b, n<<1, 1);
    for (int i = 0; i < n<<1; i++) b[i] = ((1ll*b[i]*(2-1ll*tmp[i]*b[i]%mod)%mod)+mod)%mod;
    NTT(b, n<<1, -1);
    memset(b+n, 0, sizeof(int)*n);
    // !!!
}

int A[MAXN], B[MAXN], n, m;

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
        scanf("%d", &A[i]);
    int t = 1;
    while (t < m) t <<= 1;
    get_inv(A, B, t);
    for (int i = 0; i < m; i++)
        printf("%d ", (B[i]+mod)%mod);
    return 0;
}

bzoj3625: [Codeforces Round #250]小朋友和二叉树

设方案数的生成函数为 G(z) ,集合 C 的生成函数为C(z)。考虑一棵二叉树的形态。他要么只有一个点,要么由一个点和两个子数构成。则:

G(z)=G2(z)C(z)+1

解得: G(z)=1±14C(z)C(z)

考虑正负号。题目中有 c[i]1 ,所以右边的分母常数项为0,当且仅当分子常数项也为0解才正确。因此要取符号。

分子有理化,得到:

G(z)=21+14C(z)

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

const int MAXN = (1<<18)+1;
const int mod = 998244353, g = 3;
int rev[MAXN];

int power(int a, int n)
{
    int ans = 1, b = a;
    for (register int i = 0; i <= 30; i++) {
        if ((n>>i)&1) ans = (long long)ans*b%mod;
        b = (long long)b*b%mod;
    }
    return ans;
}

int inv(int a)
{ return power(a, mod-2); }

void NTT(int a[MAXN], int n, int flag)
{
    int lg = 0;
    for (register int i = 1; i < n; i <<= 1) lg++;
    rev[0] = 0;
    for (register int i = 0; i < n; i++)
        rev[i] = (rev[i>>1]>>1)|((i&1)<<(lg-1));
    for (register int i = 0; i < n; i++)
        if (rev[i]<i) swap(a[rev[i]], a[i]);
    for (register int k = 1; k <= n; k <<= 1) {
        int dw = power(g, (mod-1)/k);
        if (flag == -1) dw = inv(dw);
        for (register int i = 0; i < n; i += k) {
            int w = 1, u, v;
            for (register int j = 0; j < k>>1; j++) {
                u = a[i+j], v = (long long)w*a[i+j+(k>>1)]%mod;
                a[i+j] = (u+v)%mod, a[i+j+(k>>1)] = ((u-v)%mod+mod)%mod, w = (long long)w*dw%mod;
            }
        }
    }
    if (flag == -1) {
        int k = inv(n);
        for (register int i = 0; i < n; i++) 
            a[i] = (long long)a[i]*k%mod;
    }
}

int tmp_inv[MAXN*2];

void get_inv(int a[], int b[], int n)
{
    if (n == 1) { b[0] = inv(a[0]); return;}
    get_inv(a, b, n>>1);
    memcpy(tmp_inv, a, sizeof(int)*n), memset(tmp_inv+n, 0, sizeof(int)*n);
    NTT(tmp_inv, n<<1, 1), NTT(b, n<<1, 1);
    for (register int i = 0; i < n<<1; i++) 
        b[i] = (1ll*b[i]*(2-1ll*tmp_inv[i]*b[i]%mod)%mod+mod)%mod;
    NTT(b, n<<1, -1);
    memset(b+n, 0, sizeof(int)*n);
}

int tmp_sqrt[MAXN]; // b^{-1}
int tmp_ntt[MAXN]; 
void get_sqrt(int a[], int b[], int n)
{
    if (n == 1) {b[0] = 1; return; } 
    get_sqrt(a, b, n>>1), memset(tmp_sqrt, 0, sizeof(int)*n*2);
    get_inv(b, tmp_sqrt, n);
    memcpy(tmp_ntt, a, sizeof(int)*n), memset(tmp_ntt+n, 0, sizeof(int)*n);
    NTT(tmp_sqrt, n<<1, 1), NTT(tmp_ntt, n<<1, 1), NTT(b, n<<1, 1);
    int val = inv(2);
    for (register int i = 0; i < n<<1; i++)
        b[i] = (1ll*b[i]*val%mod+1ll*tmp_ntt[i]*tmp_sqrt[i]%mod*val%mod)%mod;
    NTT(b, n<<1, -1), memset(b+n, 0, sizeof(int)*n);
}

int C[MAXN], n, m, d[MAXN], e[MAXN], ts[MAXN];

inline int read()
{
    int a = 0, c;
    do c = getchar(); while(!isdigit(c));
    while(isdigit(c)) {
        a = a*10+c-'0';
        c = getchar();
    }
    return a;
}

int main()
{
    scanf("%d%d", &n, &m);
    int t = 1;
    while (t <= m) t <<= 1;
    for (register int i = 1; i <= n; i++) 
        C[read()]++;
    for (register int i = 0; i < t; i++)
        C[i] = ((-4ll*C[i])%mod+mod)%mod;
    C[0]++;
    get_sqrt(C, d, t);
    d[0]++;
    get_inv(d, e, t);
    for (register int i = 1; i <= m; i++)
        printf("%d\n", 2ll*e[i]%mod);
    return 0;
}

bzoj4517: [Sdoi2016]排列计数

学习一个线性递推求逆元
http://blog.miskcoo.com/2014/09/linear-find-all-invert

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

const int MAXN = 1000005;
const int mod = 1e9+7;

int f[MAXN];
int n, m;

int facd[MAXN], ifac[MAXN], inv_num[MAXN];

void init()
{
    inv_num[1] = 1;
    for (int i = 2; i < MAXN; i++)
        inv_num[i] = (-(long long)(mod/i)*inv_num[mod%i]%mod+mod)%mod;
    facd[0] = 1, ifac[0] = 1;
    for (int i = 1; i < MAXN; i++) facd[i] = (long long)facd[i-1]*i%mod;
    for (int i = 1; i < MAXN; i++) ifac[i] = (long long)ifac[i-1]*inv_num[i]%mod;
    // for (int i = 1; i < 10; i++) cout << facd[i] << " "; puts("");
    f[0] = 1, f[1] = 0, f[2] = 1;
    for (int i = 3; i < MAXN; i++) f[i] = (long long)(i-1)*((f[i-1]+f[i-2])%mod)%mod;
}

inline int choose(int n, int m)
{ return (long long)facd[n]*ifac[m]%mod*ifac[n-m]%mod; }

inline int query(int n, int m)
{ return (long long)choose(n, m)*f[n-m]%mod; }

int main()
{
    int T; scanf("%d", &T);
    init();
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        if (n < m) puts("0");
        else printf("%d\n", query(n, m));
    }
    return 0;
}

bzoj4403: 序列统计

考虑加入 L,R 之后原数列的差分数列 a0,a2,,an ,必然满足:

kak=RL=t

用隔板法分析可知长度为 n 的方案数为:

(n+tt)

总方案数为:

1in(i+tt)=1itn(it+tt)=t<in+t(it)=in+t(it)it(it)()

用上指标求和:

()=(n+t+1t+1)(t+1t+1)=(n+t+1t+1)1

然后用线性递推求逆元,lucas定理求组合数即可。lucas公式为:

(nm)=(n/pm/p)(n mod pm mod p)

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

int T, N, L, R;
const int mod = 1e6+3;
int fac[mod], ifac[mod];
int inv[mod];

inline int choose(int n, int m)
{
    if (n < m) return 0;    
    return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod; 
}

int lucas(int n, int m, int P)
{
    if (m == 0) return 1;
    return lucas(n/P, m/P, P)*1ll*choose(n%P, m%P)%mod;
}

int main()
{
    inv[1] = 1;
    for (int i = 2; i < mod; i++) inv[i] = (-1ll*mod/i%mod*inv[mod%i]%mod+mod)%mod;
    // for (int i = 1; i <= 10; i++) cerr << 1ll*i*inv[i]%mod << endl;
    fac[0] = 1, ifac[0] = 1;
    for (int i = 1; i < mod; i++) fac[i] = (long long)fac[i-1]*i%mod;
    for (int i = 1; i < mod; i++) ifac[i] = (long long)ifac[i-1]*inv[i]%mod;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &N, &L, &R);
        printf("%d\n", (lucas(N+(R-L)+1, N, mod)-1+mod)%mod);
    }
    return 0;
}

bzoj2324: [ZJOI2011]营救皮卡丘

神题…完全不会
http://hzwer.com/4639.html

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

const int MAXN = 506, MAXM = 100005;

struct node {
    int to, next, flow, cost, neg;
} edge[MAXM*10];
int head[MAXN], top = 0;
inline void push(int i, int j, int f, int c)
{ 
    // printf("%d--%d--%d->%d\n", i, f, c, j);
    ++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top;
    ++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top; 
}

const int S = MAXN-2, T = MAXN-3, SS = MAXN-4, ST = MAXN-5;
const int inf = 1e9;
int n, m, k;

void push_lim(int i, int j, int f)
{
    push(SS, j, f, 0), push(i, ST, f, 0);
    push(i, j, inf, 0);
}

#define IN 0
#define OUT 1

int dis[MAXN], vis[MAXN], pre[MAXN], pre_edge[MAXN];
queue<int> que;

bool spfa(int &flow, int &cost)
{
    for (int i = 0; i < MAXN; i++) dis[i] = inf;
    // cout << dis[1] << endl;
    memset(vis, 0, sizeof vis);
    dis[SS] = 0, vis[SS] = 1, que.push(SS);
    while (!que.empty()) {
        int nd = que.front(); que.pop();
        // cout << nd << endl;
        //system("sleep 0.5");
        vis[nd] = 0;
        for (int i = head[nd]; i; i = edge[i].next) {
            int to = edge[i].to, f = edge[i].flow, c = edge[i].cost;
            if (!f || dis[to] <= dis[nd]+c) continue;
            dis[to] = dis[nd]+c, pre[to] = nd, pre_edge[to] = i;
            if (!vis[to])
                vis[to] = 1, que.push(to);
        }
    }
    if (dis[ST] >= inf) return 0;
    int maxf = INT_MAX;
    for (int i = ST; i != SS; i = pre[i]) maxf = min(maxf, edge[pre_edge[i]].flow);
    for (int i = ST; i != SS; i = pre[i]) {
        edge[pre_edge[i]].flow -= maxf;
        edge[edge[pre_edge[i]].neg].flow += maxf;
    }
    flow += maxf, cost += maxf*dis[ST];
    return 1;
}

void mcf(int &flow, int &cost)
{
    push(T, S, inf, 0);
    flow = cost = 0;
    while (spfa(flow, cost));
}

inline int number(int nd, int id)
{
    if (nd == 0) return n+n+1; 
    return nd+id*n; 
}

int pw[MAXN], dd[MAXN], tp = 0;
int g[155][155];

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            g[i][j] = inf;
    for (int i = 0; i <= n; i++) g[i][i] = 0;
    for (int i = 1; i <= m; i++) {
        int u, v, d;
        scanf("%d%d%d", &u, &v, &d);
        g[u][v] = g[v][u] = min(g[u][v], d);
    }
    for (int k = 0; k <= n; k++)
        for (int i = 0; i <= n; i++)
            for (int j = 0; j <= n; j++) {
                if (k <= i && k <= j)
                g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
            }
    for (int i = 0; i <= n; i++)
        for (int j = i+1; j <= n; j++)
            if (g[i][j] < inf) 
                push(number(i, OUT), number(j, IN), inf, g[i][j]);
    push(S, number(0, IN), k, 0);
    for (int i = 1; i <= n; i++) {
        push_lim(number(i, IN), number(i, OUT), 1);
        push(number(i, OUT), T, inf, 0);
    }
    push(number(n, OUT), T, k, 0);
    int f = 0, cost = 0;
    // puts("---");
    mcf(f, cost);
    cout << cost << endl;
    return 0;
}

bzoj1212: [HNOI2004]L语言

hash水过去了…
正解是AC自动机,利用fail树转移。

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

const int MAXN = 1000005;

typedef unsigned long long ull;
set<ull> st;

int dp[MAXN];
int n, m;

ull hash_val(char str[])
{
    int len = strlen(str);
    ull ans = 0;
    for (int i = 0; i < len; i++)
        ans = ans*31+str[i]-'a'+1;
    return ans;
}

char str[25], s[MAXN];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%s", str);
        st.insert(hash_val(str));
        // cout << hash_val(str) << endl;
    }
    for (int i = 1; i <= m; i++) {
        memset(dp, 0, sizeof dp);
        dp[0] = 1;
        scanf("%s", s+1);
        int len = strlen(s+1);
        for (int j = 1; j <= len; j++) {
            ull hav = 0, bs = 1;
            // cout << j << endl;
            for (int k = j; k >= max(1, j-9); k--) {
                hav = hav+bs*(s[k]-'a'+1);
                // cout << s[k] << " " << hav << endl;
                bs = bs*31;
                if (st.count(hav)) {
                    // printf("%d --> %d\n", k-1, j);
                    dp[j] |= dp[k-1];
                }
            }
        }
        int flag = 0;
        for (int j = len; j >= 1; j--) {
            if (dp[j]) {
                printf("%d\n", j);
                flag = 1;
                break;
            }
        }
        if (!flag) puts("0");
    }
    return 0;
}

AC自动机解法:

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

const int MAXN = 600;

int chl[MAXN][26], fail[MAXN], fin[MAXN], top = 0;
int root = 0;

void push(int &nd, const char *str, int len = 0)
{
    if (!nd) nd = ++top;
    if (*str == '\0') fin[nd] = len;
    else push(chl[nd][*str-'a'], str+1, len+1);
}

queue<int> que;
void init()
{
    que.push(root), fail[root] = 0;
    while (!que.empty()) {
        int nd = que.front(); que.pop();
        for (int i = 0; i < 26; i++) {
            if (!chl[nd][i]) continue;
            int p = fail[nd];
            while (p && !chl[p][i]) p = fail[p];
            if (p) fail[chl[nd][i]] = chl[p][i];
            else fail[chl[nd][i]] = root;
            que.push(chl[nd][i]);
        }
    }
}

char s[30], str[1000005];
int n, m;
int dp[1000005];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%s", s);
        push(root, s);
    }
    init();
    for (int i = 1; i <= m; i++) {
        memset(dp, 0, sizeof dp);
        dp[0] = 1;
        scanf("%s", str+1);
        int l = strlen(str+1);
        int nd = root;
        for (int i = 1; i <= l; i++) {
            while (nd && !chl[nd][str[i]-'a']) nd = fail[nd];
            if (!nd) nd = root;
            else nd = chl[nd][str[i]-'a'];
            for (int p = nd; p; p = fail[p])
                if (fin[p])
                    dp[i] |= dp[i-fin[p]];
        }
        int flag = 0;
        for (int i = l; i >= 1; i--) {
            if (dp[i]) {
                flag = 1;
                printf("%d\n", i);
                break;
            }
        }
        if (!flag) printf("0\n");
    }
    return 0;
}

bzoj3696: 化合物

直接暴力就过了???
什么鬼

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

const int MAXN = 100005, MAXS = 505;

struct node {
    int to, next;
} edge[MAXN];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }

int height[MAXN];
int dp[MAXN][MAXS];
long long ans[MAXN*5];

void dfs(int nd)
{
    height[nd] = 0, dp[nd][0] = 1;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to;
        dfs(to);
        for (int j = 0; j <= height[nd]; j++)
            for (int k = 0; k <= height[to]; k++)
                ans[j^(k+1)] += (long long)dp[nd][j]*dp[to][k];
        height[nd] = max(height[nd], height[to]+1);
        for (int j = 0; j <= height[to]; j++)
            dp[nd][j+1] += dp[to][j];
    }
}

int n;

int main()
{
    scanf("%d", &n);
    for (int i = 2; i <= n; i++) {
        int p; scanf("%d", &p);
        push(p, i);
    }
    dfs(1);
    for (int i = 0; ans[i]; i++)
        printf("%lld\n", ans[i]);
    return 0;
}

bzoj3173: [Tjoi2013]最长上升子序列

直接平衡树维护即可。
然而splay疯狂TLE,treap轻松AC…

splay(TLE):

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

const int MAXN = 100005;

int chl[MAXN][2], fa[MAXN], dat[MAXN], max_val[MAXN], top = 0;
int siz[MAXN];
int root = 0;

inline void update(int nd)
{
    if (!nd) return;
    max_val[nd] = dat[nd];
    max_val[nd] = max(max(max_val[nd], max_val[chl[nd][0]]), max_val[chl[nd][1]]);
    siz[nd] = siz[chl[nd][0]]+siz[chl[nd][1]]+1;
}

inline void zig(int nd)
{
    int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;
    int son = chl[nd][tp^1];
        fa[son] = (son!=0)*p, chl[g][tg] = (g!=0)*nd;
    fa[nd] = g, fa[p] = nd, chl[nd][tp^1] = p, chl[p][tp] = son;
    update(p), update(nd);
}

void splay(int nd, int tar_fa = 0)
{
    int p, g, tp, tg;
    while (fa[nd] != tar_fa) {
        p = fa[nd], g = fa[p];
        tp = chl[p][0] != nd, tg = chl[g][0] != p;
        if (g == tar_fa) { zig(nd); break; }
        if (tp == tg) zig(p), zig(nd);
        else zig(nd), zig(nd);
    }
    if (tar_fa == 0) root = nd;
}

int rank_of(int nd)
{
    splay(nd);
    return siz[chl[nd][0]]+1;
}

inline int find_kth(int k)
{
    int nd = root;
    while (1) {
        if (siz[chl[nd][0]]+1 == k) break;
        if (siz[chl[nd][0]]+1 > k) nd = chl[nd][0];
        else nd = chl[nd][1], k -= siz[chl[nd][0]]+1;
    }
    return nd;
}

void insert_in(int k)
{
    if (!root)
        { root = ++top, dat[root] = 1, update(root); }
    else if (k == 0)
    { splay(find_kth(1)), chl[root][0] = ++top, fa[top] = root, dat[top] = 1, update(top), update(root); }
    else if (k == siz[root])
    { splay(find_kth(k)), chl[root][1] = ++top, fa[top] = root, dat[top] = max_val[root]+1, update(top), update(root); }
    else {
        int nd = find_kth(k), r = find_kth(k+1);
        splay(nd), splay(r, root);
        chl[r][0] = ++top, fa[top] = r, dat[top] = max(max_val[chl[root][0]], dat[root])+1;
        update(top), update(r), update(root);
    }
}

int n;
int k;

inline int read()
{
    int a = 0, c;
    do c = getchar(); while (!isdigit(c));
    while (isdigit(c)) {
        a = a*10+c-'0';
        c = getchar();
    }
    return a;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        k = read();
        insert_in(k);
        printf("%d\n", max_val[root]);
    }
    return 0;
}

bzoj2286: [Sdoi2011]消耗战

复习虚树。

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

const int MAXN = 250005;
const long long inf = 23333333333333ll;

struct node {
    int to, next, dis;
} edge[MAXN*2], edge_it[MAXN*2];
int head[MAXN], top = 0;
int head_it[MAXN], top_it = 0;
inline void push(int i, int j, int d)
{ edge[++top] = (node){j, head[i], d}, head[i] = top; }

int it_kill_list[MAXN], list_top = 0;
inline void push_it(int i, int j, int d)
{
    // printf("%d--%d-->%d\n", i, d, j);
    if (!head_it[i]) it_kill_list[++list_top] = i;
    edge_it[++top_it] = (node) {j, head_it[i], d}, head_it[i] = top_it;
}

int depth[MAXN];
long long len[MAXN];
int fa[MAXN][21], dfn[MAXN], dfn_top = 0;
int out[MAXN], n, m;

void dfs(int nd, int f)
{
        fa[nd][0] = f, dfn[nd] = ++dfn_top;
    // cerr << nd << " " << dfn_top << endl;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to;
        if (to == f) continue;
        depth[to] = depth[nd]+1, len[to] = min(len[nd], (long long)edge[i].dis);
        dfs(to, nd);
    }
    out[nd] = dfn_top;
}

void init_lca()
{
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j-1]][j-1];
}

int lca(int a, int b)
{
    if (depth[a] < depth[b]) swap(a, b);
    for (int d = depth[a]-depth[b], i = 0; i <= 20; i++)
        if ((d>>i)&1)
            a = fa[a][i];
    if (a == b) return a;
    for (int i = 20; i >= 0; i--)
        if (fa[a][i] != fa[b][i])
            a = fa[a][i], b = fa[b][i];
    return fa[a][0];
}

bool cmp(int a, int b)
{ return dfn[a] < dfn[b]; }

int stk[MAXN], stk_top = 0;
int in_stk[MAXN];
int mark[MAXN];

inline bool in_subtree(int nd, int T)
{ return dfn[nd] >= dfn[T] && dfn[nd] <= out[T];}

void build_tree(vector<int> &pt)
{
    sort(pt.begin(), pt.end(), cmp);
    // puts("Building tree");
    // for (int i = 0; i < pt.size(); i++)
    //  cerr << dfn[pt[i]] << " ";
    // cerr << endl;
    for (int i = 0; i < pt.size(); i++)
        mark[pt[i]] = 1;
    mark[1] = 0;
    for (int i = 0; i < pt.size(); i++) {
        // cerr << pt[i] << endl;
        if (!stk_top) stk[++stk_top] = pt[i], in_stk[pt[i]] = 1;
        else {
            while (stk_top > 1 && !in_subtree(pt[i], stk[stk_top-1]) &&
                   !in_subtree(pt[i], stk[stk_top])) {
                in_stk[stk[stk_top]] = 0;
                push_it(stk[stk_top-1], stk[stk_top], len[stk[stk_top]]);
                stk_top--;
                // cerr << stk_top << endl;
            }
            if (in_subtree(pt[i], stk[stk_top])) stk[++stk_top] = pt[i], in_stk[pt[i]] = 1;
            else {
                int bro = stk[stk_top--], t = lca(bro, pt[i]);
                if (!stk_top || t != stk[stk_top]) stk[++stk_top] = t, in_stk[t] = 1;
                push_it(stk[stk_top], bro, len[bro]), stk[++stk_top] = pt[i], in_stk[pt[i]] = 1;
            }
        }
    }
    while (stk_top > 1) {
        // cerr << stk_top << endl;
        push_it(stk[stk_top-1], stk[stk_top], len[stk[stk_top]]);
        stk_top--;
    }
    stk_top = 0;
}

long long dfs_dp(int nd, int f)
{
    if (mark[nd]) return inf;
    long long ans = 0;
    for (int i = head_it[nd]; i; i = edge_it[i].next) {
        int to = edge_it[i].to, d = edge_it[i].dis;
        // cerr << nd << "-->" << d <<"-->" << to << endl;
        if (to == f) continue;
        ans += min((long long)d, dfs_dp(to, nd));
        if (ans > inf) ans = inf;
    }
    return ans;
}

void kill_tree(vector<int> &vec)
{
    int num = vec.size();
    for (int i = 1; i <= list_top; i++)
        head_it[it_kill_list[i]] = 0;
    list_top = 0;
    top_it = 0;
    for (int i = 0; i < num; i++)
        mark[vec[i]] = 0;
}

vector<int> vec;

int main()
{
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        int u, v, d;
        scanf("%d%d%d", &u, &v, &d);
        push(u, v, d), push(v, u, d);
    }
    scanf("%d", &m);
    for (int i = 1; i <= n; i++) len[i] = inf;
    dfs(1, 0), init_lca();
    for (int i = 1; i <= m; i++) {
        int t, u; scanf("%d", &t);
        vec.clear();
        vec.push_back(1);
        while (t--) {
            scanf("%d", &u);
            vec.push_back(u);
        }
        build_tree(vec);
        printf("%lld\n", dfs_dp(1, 0));
        kill_tree(vec);
    }
    return 0;
}

bzoj1875: [SDOI2009]HH去散步

数据范围告诉我们是矩阵乘法..

但是怎么限制不走回头路呢…

一种可行的方案是边点互换,然后在点上转移 t 次就变成了在边上转移t1次,然后就可以矩阵了。

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

const int MAXN = 125, mod = 45989;

int n, m, t, a, b;

struct node {
    int from, to, neg;
} edge[MAXN*2];
int top = 0;

struct matrix {
    int A[MAXN][MAXN];
    void clear()
    { memset(A, 0, sizeof A); }
    matrix()
    { clear(); }
    friend matrix operator * (const matrix &a, const matrix &b)
    {
        matrix ret;
        for (int i = 1; i <= top; i++)
            for (int j = 1; j <= top; j++)
                for (int k = 1; k <= top; k++)
                    (ret.A[i][j] += a.A[i][k]*b.A[k][j]%mod) %= mod;
        return ret;
    }
} g;

matrix power(matrix a, int p)
{
    matrix ans;
    for (int i = 1; i <= top; i++)
        ans.A[i][i] = 1;
    for (int i = 0; i < 31; i++) {
        if ((p>>i)&1) ans = ans*a;
        a = a*a;
    }
    return ans;
}

inline void push(int x, int y)
{
    ++top, edge[top] = (node){x, y, top+1};
    ++top, edge[top] = (node){y, x, top-1};
}

int c[MAXN], y[MAXN];

void build()
{
    for (int i = 1; i <= top; i++) {
        for (int j = 1; j <= top; j++) {
            if (i != j && j != edge[i].neg && edge[i].to == edge[j].from)
                g.A[i][j] = 1;
        }
        if (edge[i].from == a) c[i]++;
    }
}

int main()
{
    scanf("%d%d%d%d%d", &n, &m, &t, &a, &b);
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        push(u, v);
    }
    build();
    g = power(g, t-1);
    for (int i = 1; i <= top; i++) {
        for (int j = 1; j <= top; j++)
            (y[i] += c[j]*g.A[j][i]) %= mod;
    }
    int ans = 0;
    for (int i = 1; i <= top; i++)
        if (edge[i].to == b)
            (ans += y[i]) %= mod;
        printf("%d\n", ans);
    return 0;
}

bzoj4027: [HEOI2015]兔子与樱花

给定一棵树,点有权值。删掉一个点会使得该节点的权值和孩子挂到父亲,求最多删去多少个点。

考虑如下的贪心策略,递归的做各个子树,然后贪心的删除尽可能多的儿子。

这个贪心是正确的,因为不可能为了在上面删除节点而放弃删除下面的。因为在越高的地方删除难度越大..

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

const int MAXN = 2000005;

struct node {
    int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }

int n, m;
int chl[MAXN], c[MAXN];
int cnt = 0;

bool cmp(const int a, const int b)
{ return chl[a]+c[a] < chl[b]+c[b]; }

void dfs(int nd)
{
        vector<int> vec;
    for (int i = head[nd]; i; i = edge[i].next)
        dfs(edge[i].to), vec.push_back(edge[i].to);
    sort(vec.begin(), vec.end(), cmp);
    for (int i = 0; i < vec.size(); i++) {
        if (c[nd]+chl[nd]-1+chl[vec[i]]+c[vec[i]] > m) break;
        c[nd] += c[vec[i]], chl[nd] += chl[vec[i]]-1;
        cnt++;
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &c[i]);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &chl[i]);
        for (int j = 1; j <= chl[i]; j++) {
            int v; scanf("%d", &v);
            push(i, ++v);
        }
    }
    dfs(1);
    printf("%d\n", cnt);
    return 0;
}

bzoj3991: [SDOI2015]寻宝游戏

树链的并…

luogu数据太弱了吧…边界根本卡不住..

小黄鸭调试法get

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

const int MAXN = 200005;

struct node {
    int to, next;
    long long dis;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j, long long d)
{ edge[++top] = (node){j, head[i], d}, head[i] = top; }

int dfn[MAXN], dfn_top = 0;
int depth[MAXN];
long long len[MAXN];
int fa[MAXN][21];
int n, m;

void dfs(int nd, int f)
{
    dfn[nd] = ++dfn_top;
    depth[nd] = depth[f]+1, fa[nd][0] = f;
    for (int i = head[nd]; i; i = edge[i].next) {
        if (edge[i].to != f)
            len[edge[i].to] = len[nd]+edge[i].dis, dfs(edge[i].to, nd);
    }
}

void init()
{
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j-1]][j-1];
}

int lca(int a, int b)
{
    if (depth[a] < depth[b]) swap(a, b);
    for (int i = 0, d = depth[a]-depth[b]; i <= 20; i++)
        if ((d>>i)&1)
            a = fa[a][i];
    if (a == b) return a;
    for (int i = 20; i >= 0; i--)
        if (fa[a][i] != fa[b][i])
            a = fa[a][i], b = fa[b][i];
    return fa[a][0];
}

long long dis(int a, int b)
{
    int c = lca(a, b);
    return len[a]+len[b]-2*len[c];
}

class cmp {
public:
    bool operator () (const int &a, const int &b) const
    { return dfn[a] < dfn[b]; }
};
set<int, cmp, allocator<int> > st;

long long ans = 0;

void insert(int nd)
{
    if (st.empty()) { st.insert(nd); return; }
    set<int, cmp, allocator<int> >::iterator i = st.upper_bound(nd), j;
    if (i == st.begin()) 
        j = st.end(), j--;
    else if (i == st.end()) 
        i--, j = st.begin();
    else j = i, j--;
    // cerr << *i << " " << *j << " --  " << nd << " " << dis(2, 3) << endl; 
    ans -= dis(*i, *j), ans += dis(*i, nd), ans += dis(*j, nd);
    st.insert(nd);
}

void del(int nd)
{
    if (st.size() == 1) { st.erase(nd); return; }
    set<int, cmp, allocator<int> >::iterator i = st.lower_bound(nd), l, r;
    if (i == st.begin())
        l = st.end(), l--, r = i, r++;
    else if (i == --st.end())
        r = st.begin(), l = i, l--;
    else l = i, l--, r = i, r++;
    ans -= dis(*i, *l), ans -= dis(*i, *r), ans += dis(*l, *r);
    st.erase(nd); 
}

void deal(int nd)
{
    if (st.count(nd)) del(nd);
    else insert(nd);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n; i++) {
        int u, v;
        long long d;
        scanf("%d%d%lld", &u, &v, &d);
        push(u, v, d), push(v, u, d);
    }
    dfs(1, 0), init();
    for (int i = 1; i <= m; i++) {
        int t; scanf("%d", &t);
        deal(t);
        printf("%lld\n", ans);
    }
    return 0;
}

bzoj3171[Tjoi2013]循环格

坠喜欢的网络流模型!

在无源汇可行流中,一个回路是一个增广轨。对于这个题来说,显然要将图变成不相交的环,每个节点拆点并限制流量为1,改变方向需要费用,跑无源汇最小费用可行流即可。

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

const int MAXN = 20;

int g[MAXN][MAXN];

#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

int n, m;
char str[MAXN];

struct node {
    int to, next, flow, cost, neg;
} edge[MAXN*MAXN*100];
int head[MAXN*MAXN*2], top = 0;
inline void push(int i, int j, int f, int c)
{
    // if (i == 18)
    // printf("%d--%d,%d-->%d\n", i, f, c, j);
    ++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top;
    ++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top;
}

const int S = MAXN*MAXN*2-4, T = S+1;

inline void push_lim(int i, int j, int flim)
{
    push(S, j, flim, 0), push(i, T, flim, 0);
}

int dis[MAXN*MAXN*2], vis[MAXN*MAXN*2];
queue<int> que;
int pre[MAXN*MAXN*2], pre_edge[MAXN*MAXN*2];

const int inf = 23333333;

bool spfa(int &flow, int &cost)
{
    memset(dis, 127/3, sizeof dis);
    memset(vis, 0, sizeof vis);
    vis[S] = 1, dis[S] = 0, que.push(S);
    while (!que.empty()) {
        int nd = que.front(); que.pop(), vis[nd] = 0;
        for (int i = head[nd]; i; i = edge[i].next) {
            int to = edge[i].to, f = edge[i].flow, c = edge[i].cost;
            if (!f || dis[to] <= dis[nd]+c) continue;
            dis[to] = dis[nd]+c, pre[to] = nd, pre_edge[to] = i;
            if (!vis[to])
                vis[to] = 1, que.push(to);
        }
    }
    if (dis[T] >= inf) return 0;
    int maxf = inf;
    for (int i = T; i != S; i = pre[i]) maxf = min(maxf, edge[pre_edge[i]].flow);
    // cerr << maxf << " " << dis[T] << endl;
    for (int i = T; i != S; i = pre[i]) {
        edge[pre_edge[i]].flow -= maxf;
        edge[edge[pre_edge[i]].neg].flow += maxf;
        // cerr << "Arg : " << pre[i] << "-->"<<  i << " Dis = "<< edge[pre_edge[i]].cost << endl;
    }
    flow += maxf, cost += dis[T]*maxf;
        return 1;
}

void mcf(int &flow, int &cost)
{
    flow = cost = 0;
    while (spfa(flow, cost));
}

inline int number(int i, int j, int id)
{ return (i-1)*m+j+id*(n*m); }

#define IN 0
#define OUT 1

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%s", str+1);
        for (int j = 1; j <= m; j++) {
            switch(str[j]) {
            case 'U' : g[i][j] = UP; break;
            case 'L' : g[i][j] = LEFT; break;
            case 'R' : g[i][j] = RIGHT; break;
            case 'D' : g[i][j] = DOWN; break;
            }
        }
    }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            push_lim(number(i, j, IN), number(i, j, OUT), 1);
            if (g[i][j] == UP) {
                push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 0);
                push(number(i, j, OUT), number(i<n?i+1:1, j, IN), 1, 1);
                    push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 1);
                push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 1);
            } else if (g[i][j] == DOWN) {
                push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 1);
                push(number(i, j, OUT), number(i<n?i+1:1, j, IN), 1, 0);
                push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 1);
                push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 1);
            } else if (g[i][j] == LEFT) {
                push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 1);
                push(number(i, j, OUT), number(i<n?i+1:1, j, IN), 1, 1);
                push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 0);
                push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 1);
            } else if (g[i][j] == RIGHT) {
                push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 1);
                push(number(i, j, OUT), number(i<n?i+1:1, j, IN), 1, 1);
                push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 1);
                push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 0);
            } 
        }
    int flow, cost;
    mcf(flow, cost);
    cout << cost << endl;
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值