1. 程式人生 > >NAIPC 2016 【5/11】

NAIPC 2016 【5/11】

題目連結

B - Alternative Bracket Notation

給出了一種表示括號匹配的方法,要求對於給定的括號序列求出長度最小的表達。

通過觀察這種方法可以發現,它的基本實現過程仍然是不斷地把左括號扔進棧裡,然後每次遇到右括號的時候取出來。一對括號在表示式中佔的長度分為三部分,左右端點的數字長度以及2個字元的特殊字元。

用la[i]和lb[i]表示第i個位置上的括號,它左端點和右端點的數字長度。初始的時候顯然都為1,然後在將左括號扔進去的時候,左端點的值應該是當前的序列長度+兩個數字的長度+2,並更新當前序列長度。

這樣處理完一遍後,會出現一個問題,就是一個位置上的數的長度增加以後,會對前面的某些數產生影響。這時候就對每對括號的左右端點,檢查端點數字的實際長度與記錄的端點數字長度是否相同

。重複計算直到所有位置數字都合法為止。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include <cstring>
#include <iostream>
#include<map>
using namespace std;

const int maxn = 4050;

int n, numl[maxn*15];
int la[maxn], lb[maxn];
int sta[maxn], L[maxn], R[maxn];
char s[maxn];

void init()
{
    numl[0] = 0;
    for(int i = 1;i < maxn*15;i++)
        numl[i] = numl[i/10] + 1;
}

int main()
{
    init();
    scanf("%s", s);
    n = strlen(s);
    for(int i = 0;i < n;i++) la[i] = lb[i] = 1;
    while(1)
    {
        int top = 0, cur = 0;
        for(int i = 0;i < n;i++)
        {
            if(s[i] == '(')
            {
                cur += la[i] + lb[i] + 2;
                L[i] = cur;
                sta[top++] = i;
            }
            else R[sta[--top]] = cur;
        }
        bool flag = 1;
        for(int i = 0;i < n;i++)
        {
            if(s[i] == '(')
            {
                if(numl[L[i]] != la[i] || lb[i] != numl[R[i]])
                {
                    flag = 0;
                    break;
                }
            }
        }
        if(flag) break;
        for(int i = 0;i < n;i++)
        {
            if(s[i] == '(')
                la[i] = numl[L[i]], lb[i] = numl[R[i]];
        }
    }
    for(int i = 0;i < n;i++)
    {
        if(s[i] == '(')
            printf("%d,%d:", L[i], R[i]);
    }
    printf("\n");
    return 0;
}

C - Greetings!

有n種大小不同的矩形,現在需要選擇k種任意大小的矩形,它們全部覆蓋住,使得浪費的面積最小。

1<=n,k<=15,一開始想搜尋,然後O(15!)就TLE了……

正解是狀壓dp,dp[i][S]表示用i種矩形覆蓋S這個集合浪費的最小面積。

for(int k = p;k;k = (k-1)&p) 這個操作可以列舉二進位制p的所有子集,列舉所有方案的所有子集的時間複雜度是O(3^{n})。學到了Orz

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;

const int maxn = 16;
const ll INF = (1LL << 62) - 1;

int n, k;
ll dp[maxn][1<<maxn], val[1<<maxn], sum[1<<maxn];
int H[1<<maxn], W[1<<maxn], num[1<<maxn];
struct node
{
    int h, w;
    int cnt;
}e[maxn];

int main()
{
    scanf("%d%d", &n, &k);
    for(int i = 0;i < n;i++)
    {
        scanf("%d%d%d", &e[i].w, &e[i].h, &e[i].cnt);
    }
    memset(sum, 0, sizeof(sum));
    for(int i = 0;i < (1<<n);i++)
    {
        for(int j = 0;j < n;j++)
        {
            if((i>>j) & 1)
            {
                H[i] = max(H[i], e[j].h);
                W[i] = max(W[i], e[j].w);
                sum[i] += 1LL*e[j].h*e[j].w*e[j].cnt;
                num[i] += e[j].cnt;
            }
        }
        val[i] = 1LL*H[i]*W[i]*num[i] - sum[i];
    }
    for(int i = 0;i <= k;i++)
    {
        for(int j = 0;j < (1<<n);j++)
            dp[i][j] = INF;
    }
    dp[0][0] = 0;
    for(int i = 1;i <= k;i++)
    {
        for(int j = 0;j < (1<<n);j++)
        {
            ll tmp = INF;
            for(int p = j;p;p = (p-1) & j)
            {
                if(dp[i-1][j-p] < INF)
                    tmp = min(tmp, dp[i-1][j-p] + val[p]);
            }
            dp[i][j] = tmp;
        }
    }
    ll ans = INF;
    for(int i = 0;i <= k;i++)
        ans = min(ans, dp[i][(1<<n)-1]);
    printf("%I64d\n", ans);
    return 0;
}

E - K-Inversions

給出一個只含有A和B的字串,定義如果si為B,sj為A, 且j-i=k,這就產生了一個k-Inversion。求1~n-1的Inversion數目。

考慮當j > i時,也就是說,j + (n-i) > n。構造兩個多項式,A[i] = (s[i] == 'B'),B[n-i-1] = (s[i] == 'A'),FFT加速卷積取n~n*2-1項的係數即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const ll INF = (1LL << 62) - 1;
const double pi = acos(-1.0);
const int maxn = 6000050;

int n, len, ans[maxn];
char s[maxn];
struct Complex
{
    double x, y;
    Complex (double _x = 0.0, double _y = 0.0)
    {
        x = _x;
        y = _y;
    }
    Complex operator - (const Complex &o) const
    {
        return Complex(x - o.x, y - o.y);
    }
    Complex operator + (const Complex &o) const
    {
        return Complex(x + o.x, y + o.y);
    }
    Complex operator * (const Complex &o) const
    {
        return Complex(x*o.x - y*o.y, x*o.y + y*o.x);
    }
}a[maxn], b[maxn];

void change(Complex y[], int len)
{
    int i, j, k;
    for(i = 1, j = len/2;i < len-1;i++)
    {
        if(i < j) swap(y[i], y[j]);
        k = len/2;
        while(j >= k)
        {
            j -= k;
            k /= 2;
        }
        if(j < k) j += k;
    }
}

void FFT(Complex y[], int len, int op)
{
    change(y, len);
    for(int h = 2;h <= len;h <<= 1)
    {
        Complex wn(cos(-op*2*pi/h), sin(-op*2*pi/h));
        for(int j = 0;j < len;j += h)
        {
            Complex w(1, 0);
            for(int k = j;k < j + h/2;k++)
            {
                Complex u = y[k];
                Complex t = w*y[k + h/2];
                y[k] = u + t;
                y[k + h/2] = u - t;
                w = w*wn;
            }
        }
    }
}

int main()
{
    scanf("%s", s);
    n = strlen(s);
    len = 1;
    while(len < n*2) len <<= 1;
    for(int i = 0;i < n;i++)
    {
        if(s[i] == 'A')
        {
            a[n-i-1] = Complex(0, 0);
            b[i] = Complex(1, 0);
        }
        else
        {
            a[n-i-1] = Complex(1, 0);
            b[i] = Complex(0, 0);
        }
    }
    for(int i = n;i < len;i++)
        a[i] = b[i] = Complex(0, 0);
    FFT(a, len, 1);
    FFT(b, len, 1);
    for(int i = 0;i < len;i++)
        a[i] = a[i]*b[i];
    FFT(a, len, -1);
    for(int i = 0;i < len;i++)
        ans[i] = (int)(a[i].x/len + 0.5);
    for(int i = 0;i < n - 1;i++)
        printf("%d\n", ans[i + n]);
    return 0;
}

F - Mountain Scenes

將n個1*1方塊堆到h*w格子裡,問有多少種不同的出現山峰的方案。

dp[i][j]表示前i列放了j個的方案數,dp[i][j+k] = Sum{dp[i-1][j]}轉移,求出所有的方案數,最後減去沒有山峰的\left \lfloor \frac{n}{w} \right \rfloor種情況即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const ll INF = (1LL << 62) - 1;
const ll mod = 1e9 + 7;

int n, w, h;
ll dp[105][10005];

int main()
{


    scanf("%d%d%d", &n, &w, &h);
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for(int i = 1;i <= w;i++)
    {
        for(int j = 0;j <= n;j++)
        {
            if(dp[i-1][j] == 0) continue;
            for(int k = 0;k <= h;k++)
            {
                if(j + k > n) break;
                dp[i][j+k] += dp[i-1][j];
                dp[i][j+k] %= mod;
            }
        }
    }
    ll ans = 0;
    for(int i = 0;i <= n;i++)
        ans = (ans + dp[w][i]) % mod;
    for(int i = 0;i <= h;i++)
    {
        if(i*w <= n)
            ans--;
    }
    ans = (ans + mod) % mod;
    printf("%I64d\n", ans);
    return 0;
}

I - Tourists

在樹上求k到k的倍數的路徑長度之和。

直接暴力列舉所有的邊用LCA計算即可。時間複雜度O(nlog^{2}n)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const int maxn = 200050;
const ll INF = (1LL << 62) - 1;

int n, a, b, no;
int pre[maxn][35], dep[maxn];
int head[maxn];
bool vis[maxn];
struct node
{
    int to;
    int nxt;
}e[maxn << 1];
void add(int a, int b)
{
    e[no].to = b, e[no].nxt = head[a];
    head[a] = no++;
    e[no].to = a, e[no].nxt = head[b];
    head[b] = no++;
}

void dfs(int u, int fa, int num)
{
    pre[u][0] = fa;
    dep[u] = num;
    vis[u] = 1;
    for(int i = head[u];i != -1;i = e[i].nxt)
    {
        int v = e[i].to;
        if(vis[v]) continue;
        dfs(v, u, num + 1);
    }
    return;
}

void init_lca()
{
    for(int j = 1;(1<<j) <= n;j++)
    {
        for(int i = 1;i <= n;i++)
            pre[i][j] = -1;
    }
    for(int j = 1;(1<<j) <= n;j++)
    {
        for(int i = 1;i <= n;i++)
        {
            if(pre[i][j - 1] != -1)
                pre[i][j] = pre[pre[i][j - 1]][j - 1];
        }
    }
}

int lca(int x, int y)
{
    if(dep[x] < dep[y]) swap(x, y);
    int mlg = 0;
    while((1<<mlg) <= dep[x]) mlg++;
    mlg--;
    for(int i = mlg;i >= 0;i--)
    {
        if(dep[x] - (1<<i) >= dep[y])
           x = pre[x][i];
    }
    if(x == y) return x;
    for(int i = mlg;i >= 0;i--)
    {
        if(pre[x][i] != -1 && pre[x][i] != pre[y][i])
            x = pre[x][i], y = pre[y][i];
    }
    return pre[x][0];
}

int main()
{
    scanf("%d", &n);
    no = 0;
    memset(head, -1, sizeof(head));
    for(int i = 1;i < n;i++)
    {
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    dfs(1, 0, 1);
    ll ans = 0;
    init_lca();
    for(int i = 1;i <= n;i++)
    {
        for(int j = i*2;j <= n;j += i)
        {
            int tp = lca(i, j);
            ans += 1LL*(dep[i] - dep[tp] + dep[j] - dep[tp] + 1);
        }
    }
    printf("%I64d\n", ans);
    return 0;
}