각종 OJ 문제 풀이 기록 6.9 - 6.15

94259 단어 bzoj
각종 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 
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)<1));
    for (int i = 0; i < n; i++)
        if (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;
}

꼬마 와 이 진 트 리
프로젝트 수의 생 성 함 수 는 G (z) 이 고 집합 C 의 생 성 함 수 는 C (z) 입 니 다.이 진 트 리 의 형 태 를 고려 하 다.그 는 점 이 하나 밖 에 없 거나 점 하나 와 두 개의 키 로 구성 되 어 있다.즉:
G(z)=G2(z)C(z)+1
해 득: G (z) = 1 ± 1 − 4C (z) √ C (z)
플러스 와 마이너스 번 호 를 고려 하 다.제목 에 c [i] ≥ 1 이 있 기 때문에 오른쪽 에 있 는 분모 상수 항 은 0 이 고 분자 상수 항 만 0 해 가 되 어야 정확 하 다.그래서 부 호 를 취해 야 한다.
분자 유리화
G(z)=21+1−4C(z)−−−−−−−−√
#include 
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)<1));
    for (register int i = 0; i < n; i++)
        if (rev[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
"
, 2ll*e[i]%mod); return 0; }

bzoj 4517: [Sdoi 2016] 배열 계수
선형 전달 역 원 을 학습 하 다.http://blog.miskcoo.com/2014/09/linear-find-all-invert
#include 
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
"
, query(n, m)); } return 0; }

bzoj 4403: 서열 통계
L, R 가입 을 고려 한 후 원래 수열 의 차이 점 수 는 10216 ° a0, a2,..., an * 10217 ° 이 고 반드시 만족 할 것 입 니 다.
∑kak=R−L=t
칸막이 법 으로 길이 가 n 인 방안 수 를 분석 하면 다음 과 같다.
(n+tt)
총 프로젝트 수:
∑1≤i≤n(i+tt)=∑1≤i−t≤n(i−t+tt)=∑t지표 로 화 해 를 구하 다.
(∗)=(n+t+1t+1)−(t+1t+1)=(n+t+1t+1)−1
그 다음 에 선형 전달 으로 역 원 을 추구 하고 luca 정리 로 조합 수 를 구하 면 된다.luca 공식 은:
(nm)=(n/pm/p)(n mod pm mod p)
#include 
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
"
, (lucas(N+(R-L)+1, N, mod)-1+mod)%mod); } return 0; }

피 카 츄 구출
신 제 는... 전혀 못 해 요.http://hzwer.com/4639.html
#include 
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
", 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; }

bzoj 1212: [HNOI 2004] L 언어
hash 물이 지 나 갔 습 니 다.
#include 
using namespace std;

const int MAXN = 1000005;

typedef unsigned long long ull;
set 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
", k-1, j);
dp[j] |= dp[k-1]; } } } int flag = 0; for (int j = len; j >= 1; j--) { if (dp[j]) { printf("%d
"
, j); flag = 1; break; } } if (!flag) puts("0"); } return 0; }

AC 자동 해법:
#include 
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
"
, i); break; } } if (!flag) printf("0
"
); } return 0; }

화합물
그냥 폭력 이면 넘 어 가 는 거 야??무슨 귀신
#include 
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
"
, ans[i]); return 0; }

bzoj 3173: [Tjoi 2013] 최 장 상승 서브 시퀀스
직접 밸 런 스 트 리 를 유지 하면 됩 니 다.하지만 splay 미 친 TLE, treap 가 벼 운 AC...
splay(TLE):
#include 
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
"
, max_val[root]); } return 0; }

소모 전
허수 수 를 복습 하 다.
#include 
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
", 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
"
, dfs_dp(1, 0)); kill_tree(vec); } return 0; }

bzoj 1875: [SDOI 2009] HH 산책 가요.
데이터 범 위 는 우리 에 게 행렬 곱셈 이 라 고 알려 준다.
근 데 왜 돌아 오지 않 는 걸 제한해?
하나의 실행 가능 한 방안 은 변 점 을 교환 한 후에 점 에서 t 번 을 옮 기 면 변 에서 t - 1 번 을 옮 기 고 행렬 을 할 수 있다 는 것 이다.
#include 
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
"
, ans); return 0; }

토끼 와 벚꽃
나무 한 그루 를 정 하 다.한 점 을 삭제 하면 이 노드 의 가중치 와 아 이 를 아버지 에 게 걸 고 최대 몇 점 을 삭제 해 야 합 니까?
다음 과 같은 욕심 전략 을 고려 해 키 트 리 를 재 귀적 으로 만 들 고 최대한 많은 아들 을 욕심 내 서 삭제 합 니 다.
이 욕심 은 옳 은 것 이다. 위 에서 노드 를 삭제 하기 위해 아래 의 것 을 삭제 하 는 것 을 포기 할 수 없 기 때문이다.높 은 곳 일수 록 삭제 가 어렵 기 때문에...
#include 
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
"
, cnt); return 0; }

[SDOI 2015] 보물 찾기 게임
나무 사슬 과...
luogu 데이터 가 너무 약 하 잖 아.. 경계 가 안 걸 려..
오리 새끼
#include 
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
"
, ans); } return 0; }

bzoj 3171 [Tjoi 2013] 순환 격
좋아 하 는 네트워크 스 트림 모델!
무 원 환 실행 가능 흐름 에서 하나의 회 로 는 확장 궤도 이다.이 문제 에 있어 그림 을 교차 하지 않 는 고리 로 만들어 야 한다. 각 노드 의 철거 점 과 유량 을 1 로 제한 하고 방향 을 바 꾸 는 데 비용 이 필요 하 며 원천 송금 이 없 는 최소 비용 으로 흐 르 면 된다.
#include 
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
"
, 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(i1: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(i1: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(i1: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(i1: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; }

좋은 웹페이지 즐겨찾기