1. 程式人生 > 其它 >noip2021訓練4

noip2021訓練4

Link

SCOI2003 嚴格N元樹

高精度板子(?

不過 dp 方程還是挺難想到的

\(f_i\) 表示深度 \(\le i\)\(n\) 元樹的個數,那麼答案即為 \(f_d-f_{d-1}\)

這就是差分的頂級理解

考慮如何轉移

對於 \(f_i\),可以理解成 \(f_{i-1}\) 再加上一個根,這個根有 \(n\) 個子樹,每個子樹都有 \(f_{i-1}\) 種方案,還有一種只有根的。

所以 \(f_i=f_{i-1}^n+1\)

然後套個高精度即可

Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct INT
{
    int a[210];
    
    INT()
    {
        memset(a, 0, sizeof(a));
    }
    
    void rd()
    {
        char s[210];
        scanf("%s", s);
        a[0] = strlen(s);
        for(int i = 1; i <= a[0]; i++)
            a[i] = s[a[0] - i] - '0';
        return;
    }
    
    void print()
    {
        for(int i = a[0]; i >= 1; i--)
            cout << a[i];
        cout << endl;
        return;
    }
    
    INT operator + (const INT &b) const
    {
        INT c;
        c.a[0] = max(a[0], b.a[0]);
        for(int i = 1; i <= c.a[0]; i++)
        {
            c.a[i] += a[i] + b.a[i];
            c.a[i + 1] += c.a[i] / 10;
            c.a[i] %= 10;
        }
        if(c.a[c.a[0] + 1]) c.a[0]++;
        return c;
    }
    
    bool operator < (const INT &b) const
    {
        if(a[0] != b.a[0]) return a[0] < b.a[0];
        for(int i = a[0]; i >= 1; i--)
            if(a[i] != b.a[i]) return a[i] < b.a[i];
        return false;
    }
    
    INT operator - (INT b) const
    {
        INT t;
        memcpy(t.a, a, sizeof(a));
        for(int i = 1; i <= t.a[0]; i++)
        {
            if(t.a[i] < b.a[i]) t.a[i] += 10,  t.a[i + 1]--;
            t.a[i] -= b.a[i];
        }
        while(t.a[0] > 1 && !t.a[t.a[0]]) t.a[0]--;
        return t;
    }
    
    INT operator * (const INT &b) const
    {
        INT c;
        for(int i = 1; i <= a[0]; i++)
        {
            int w = 0;
            for(int j = 1; j <= b.a[0]; j++)
            {
                c.a[i + j - 1] += a[i] * b.a[j] + w;
                w = c.a[i + j - 1] / 10;
                c.a[i + j - 1] %= 10;
            }
            c.a[i + b.a[0]] += w;
        }
        c.a[0] = a[0] + b.a[0];
        if(!c.a[c.a[0]]) c.a[0]--;
        return c;
    }

    INT operator / (const int &b) const
    {
        INT c;
        memcpy(c.a, a, sizeof(a));
        for(int i = a[0]; i > 1; i--)
        {
            c.a[i - 1] += (c.a[i] % b) * 10;
            c.a[i] /= b;
        }
        c.a[1] /= b;
        while(!c.a[c.a[0]]) c.a[0]--;
        return c;
    }

    void operator = (int b)
    {
        while(b) a[++a[0]] = b % 10, b /= 10;
    }
} f[20];

int n, d;

int main()
{
    scanf("%d%d", &n, &d);
    f[0] = 1;
    for(int i = 1; i <= d; i++)
    {
        f[i] = 1;
        for(int j = 1; j <= n; j++)
            f[i] = f[i] * f[i - 1];
        f[i] = f[i] + f[0];
    }
    (f[d] - f[d - 1]).print();
    return 0;
}
// A.S.

ZJOI2008 騎士

基環樹上 dp 板子題

只需要在環上斷一條邊,這條邊的兩個端點只能選一個,以兩個點為根分別 dp,然後將答案取個 \(max\)

dp 的時候就是沒有上司的舞會

注意這是一個基環樹森林,所以要列舉一遍每個點。

Code
#include <bits/stdc++.h>
#define ll long long
#define INF 1e18

using namespace std;

namespace IO
{
    template <typename T>
    void read(T &x)
    {
        x = 0; int f = 1; char c = getchar();
        while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
        while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
        x *= f;
    }

    template <typename T>
    void write(T x)
    {
        if(x < 0) putchar('-'), x = -x;
        if(x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace IO;

const int N = 1e6 + 5;
int n, a[N], fa[N];
vector <int> g[N];
bool vis[N];
ll f[N][2], ans;

void dp(int u, int rt)
{
    vis[u] = 1;
    f[u][0] = 0, f[u][1] = a[u];
    for(auto v : g[u])
    {
        if(v == rt)
        {
            f[v][1] = -INF;
            continue;
        }
        dp(v, rt);
        f[u][0] += max(f[v][0], f[v][1]);
        f[u][1] += f[v][0];
    }
    return;
}

void solve(int x)
{
    vis[x] = 1;
    while(!vis[fa[x]])
    {
        x = fa[x];
        vis[x] = 1;
    }
    dp(x, x);
    ll t = max(f[x][0], f[x][1]);
    x = fa[x];
    dp(x, x);
    ans += max(t, max(f[x][0], f[x][1]));
    return;
}

int main()
{
    read(n);
    for(int i = 1; i <= n; i++)
    {
        read(a[i]), read(fa[i]);
        g[fa[i]].push_back(i);
    }
    for(int i = 1; i <= n; i++)
        if(!vis[i]) solve(i);
    write(ans), putchar('\n');
    return 0;
}
// A.S.

JSOI2008 球形空間產生器

根據,\(d=\sqrt{\sum_{i=1}^n(a_i-b_i)^2}\)

可得,\(d^2=\sum_{i=1}^n(a_i-b_i)^2\)

化簡得,\(d^2=\sum_{i=1}^n(a_i^2+b_i^2-2a_ib_i)\)

移項得,\(\sum_{i=1}^n(-2a_ib_i)+r^2+\sum_{i=1}^nb_i^2=\sum_{i=1}^na_i^2\)

其中 \(b_i\) 是未知數

注意到,\(r^2+\sum_{i=1}^nb_i^2\) 在每個方程中都有,因此可以設為一個係數為 \(1\) 的未知數,這樣就會被消掉了。

然後就可以愉快的高斯消元了 OvO

Code
#include <bits/stdc++.h>
#define db double

using namespace std;

const int N = 15;
int n;
db a[N][N];

void gauss()
{
    for(int i = 1; i <= n; i++)
    {
        if(!a[i][i])
        {
            int j;
            for(j = i + 1; j <= n; j++)
                if(a[j][i]) break;
            for(int k = 1; k <= n + 1; k++)
                swap(a[i][k], a[j][k]);
        }
        for(int k = i + 1; k <= n; k++)
            for(int j = n + 1; j >= i; j--)
                a[k][j] -= a[k][i] / a[i][i] * a[i][j];
    }
    for(int i = n; i >= 1; i--)
    {
        for(int j = i + 1; j <= n; j++)
            a[i][n + 1] -= a[i][j] * a[j][n + 1];
        a[i][n + 1] /= a[i][i];
    }
    return;
}

int main()
{
    scanf("%d", &n), n++;
    for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j < n; ++ j)
        {
            scanf("%lf", &a[i][j]);
            a[i][n + 1] -= a[i][j] * a[i][j];
            a[i][j] *= -2.0;
        }
        a[i][n] = 1;
    }
    gauss();
    for(int i = 1; i < n; ++ i){
        printf("%.3lf",a[i][n + 1]);
        if(i < n - 1) printf(" ");
    }
}
// A.S.

NOI2015 軟體包管理器

樹剖板子題

鏈上修改查詢、子樹修改查詢

Code
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define ls rt<<1
#define rs rt<<1|1

using namespace std;

const int N = 1e5 + 5;

int read()
{
    int x = 0;
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

int n,m;
vector <int> G[N];
int dep[N],siz[N],son[N],fa[N];

void dfs1(int u,int f)
{
    dep[u] = dep[f] + 1;
    siz[u] = 1;
    fa[u] = f;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(v == f) continue;
        dfs1(v,u);
        siz[u] += siz[v];
        if(siz[son[u]] < siz[v]) son[u] = v;
    }
}

int cnt,id[N],top[N];

void dfs2(int u,int tf)
{
    id[u] = ++cnt;
    top[u] = tf;
    if(son[u]) dfs2(son[u],tf);
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v,v);
    }
}

int sum[N << 2], tag[N << 2];

void pushup(int rt)
{
    sum[rt] = sum[ls] + sum[rs];
}

void pushdown(int l,int r,int rt)
{
    if(tag[rt] != -1)
    {
        int mid = (l + r) >> 1;
        sum[ls] = tag[rt] * (mid - l + 1);
        tag[ls] = tag[rt];
        sum[rs] = tag[rt] * (r - mid);
        tag[rs] = tag[rt];
        tag[rt] = -1;
    }
}

void update(int L,int R,int v,int l,int r,int rt)
{
    if(l > R || r < L) return;
    if(L <= l && r <= R)
    {
        sum[rt] = v * (r - l + 1);
        tag[rt] = v;
        return;
    }
    pushdown(l,r,rt);
    int mid = (l + r) >> 1;
    update(L,R,v,l,mid,ls);
    update(L,R,v,mid + 1,r,rs);
    pushup(rt);
}

void upd1(int x)
{
    while(top[x] != 1)
    {
        update(id[top[x]], id[x], 1, 1, n ,1);
        x = fa[top[x]];
    }
    update(id[top[x]], id[x], 1, 1, n, 1);
}

void upd2(int x)
{
    update(id[x], id[x] + siz[x] - 1, 0, 1, n, 1);
}

int main()
{
    n = read();
    for(int i = 1; i < n; i++)
    {
        int u = read() + 1;
        G[u].push_back(i + 1);
    }
    dfs1(1,0);
    dfs2(1,1);
    memset(tag,-1,sizeof(tag));
    m = read();
    while(m--)
    {
        char op[10];
        scanf("%s",op);
        int x = read() + 1, ans = sum[1];
        if(op[0] == 'i')
        {
            upd1(x);
            printf("%d\n",sum[1] - ans);
        }
        else
        {
            upd2(x);
            printf("%d\n",ans - sum[1]);
        }
    }
    return 0;
}
// A.S.

NOI2014 動物園

根據 \(kmp\) 求出的 \(nxt\) 陣列進行計算。

但是為了避免在 \(aaa...a\) 時複雜度退化為 \(O(n^2)\),可以像求 \(nxt\) 一樣用指標記錄一下,如果在 \(i-1\) 時合法,那麼在 \(i\) 時也合法。

Code
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

using namespace std;

const int N = 1e6 + 5;
const int p = 1e9 + 7;
char s[N];
int nxt[N],num[N];

signed main()
{
    int T;
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%s",s + 1);
        int n = strlen(s + 1);
        num[0] = 0, num[1] = 1;
        memset(nxt,0,sizeof(nxt));
        for(int i = 2, j = 0; i <= n; i++)
        {
            while(j && s[i] != s[j + 1]) j = nxt[j];
            if(s[i] == s[j + 1]) j++;
            nxt[i] = j;
            num[i] = num[j] + 1;
        }
        int ans = 1;
        for(int i = 1, j = 0; i <= n; i++)
        {
            while(j && s[i] != s[j + 1]) j = nxt[j];
            if(s[i] == s[j + 1]) j++;
            while(j && j * 2 > i) j = nxt[j];
            ans = ans * (num[j] + 1) % p;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
// A.S.

不想寫了,晚上回去再寫

$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$