NEERC Southern Subregional 2012

NEERC Southern Subregional 2012

Problem B. Chess Championship

題目描述:有兩個序列\(a, b\),兩個序列都有\(n\)個數,而且這\(2n\)個數兩兩不一樣,如今要將這兩個序列裏的數兩兩配對,組成\(n\)個數對,要求數對中\(a\)的數比\(b\)的數大的數對個數要比其它的多\(m\)個。問方案數。c++

solution
將這\(2n\)個數從小到大排,預處理出前\(i\)個數中\(a, b\)的個數\(suma, sumb\), \(f[i][j][k]\)表示前\(i\)個數,匹配了\(j\)\(a>b\)的數對,還有\(k\)\(a\)未匹配。
若是第\(i\)個數是\(a\)的數:數組

  1. 與前面的一個尚未匹配的\(b\)匹配,\(f[i+1][j+1][k]+=f[i][j][k]*(sumb[i-1]-suma[i-1]+k)\)
  2. 留着讓後面的\(b\)匹配,\(f[i+1][j][k+1]+=f[i][j][k]\)
    若是第\(i\)個數是\(b\)的數:
  3. 與前面一個尚未匹配的\(a\)匹配,\(f[i+1][j][k-1]+=f[i][j][k]*k\)
  4. 留着讓後面的\(a\)匹配,\(f[i+1][j][k]+=f[i][j][k]\)

答案就是\(f[2*n+1][0][0]\),由於空間不夠,全部要有滾動數組,並且卡常,因此要控制好枚舉的範圍。spa

時間複雜度:\(O(n^3)\)code

Problem E. Dragons and Princesses

題目描述:有\(n\)個格子,每一個格子上有一條龍或一個公主,每條龍守着必定數量的金幣,如今要從第一個格子出發,遇到龍時能夠選擇跳過,也能夠選擇殺死龍並拿走金幣,若是遇到公主,假如殺龍的數量大於等於公主的要求,那麼就要娶這個公主,已知最後一個格子是一個公主,而且娶且只娶最後一個公主,問最多能拿多少金幣,輸出方案。排序

solution
首先從後往前,算出到達每一個格子時最多能殺多少條龍,而後用一個堆維護最大的那幾條龍便可。ip

時間複雜度:\(O(nlogn)\)字符串

Problem F. Dumbbells

題目描述:有\(n\)個球,每一個球的重量爲\(w_i\),價值爲\(c_i\),如今要取儘可能多的集合,每一個集合必需要有\(k\)個球,而且\(k\)個球的重量不一樣,並且每一個集合的重量集合必須相同。問在最多能構成多少個集合,而且最大價值爲多少。it

solution
統計每種重量的球有多少,從大到小排序,而後取前\(k\)種重量,對應的集合數爲第\(k\)中重量的球的個數\(s\),而後算出球數大於等於\(s\)的重量中每種重量的最貴的\(s\)個球的總價值,而後取價值最大的\(k\)中重量。io

時間複雜度:\(O(nlogn)\)class

Problem G. Database optimization

題目描述:有\(n\)個物品,每一個物品有不超過\(4\)種性質,有\(m\)個詢問,每次詢問擁有某些性質的物品有多少。

solution
性質的集合不超過\(n*2^4\)種,預處理出這些集合各有多少個物品擁有便可,用一個\(map\)就行了。

時間複雜度:\(O(2^4n+m)\)

Problem H. Sultan's Pearls

題目描述:有一串珠子放在桌上,一共有\(n\)個珠子,其中有\(m\)個懸在桌邊,每一個珠子有重量和價值,如今一次能夠從兩邊取一個珠子(桌上或懸掛),若是取的是懸掛的珠子,則要從桌上移一個珠子使懸掛的珠子總數不變,當懸掛的珠子的總重量大於桌上的珠子的總重量乘\(k\)時,珠子會掉下來,問在保證珠子不掉下來的狀況下,能拿走的最大總價值,輸出方案。

solution
方案顯然是先拿走懸掛的,而後再拿在桌上的。因此能夠枚舉最終懸掛的珠子的最下面一個是哪一個,而後二分出桌面上最多能拿走多少個珠子,而後貪心地拿就好。

時間複雜度:\(O(nlogn)\)

Problem J. Ternary Password

題目描述:有一個只有\(0, 1, 2\)的字符串,如今能夠修改某些位置的字符,使得最終有\(a\)\(0\)\(b\)\(1\),問最少須要修改多少個字符,而且輸出修改後的字符串。

solution
貪心。

時間複雜度:\(O(n)\)

Problem K. Tree Queries Online

題目描述:給定一棵樹,樹有邊權,不斷地刪掉一條邊,輸出刪掉的邊的權值\(val\),取出分開的兩棵樹中點數較少的樹(若是相同則取最小編號的點所在的樹),這棵樹的邊權乘\(val\),另外一棵樹的邊權加\(val\)。強制在線。

solution
啓發式拆樹,拆的時候同時擴展兩棵樹,有一棵樹搜完就停下,而後把這棵樹的邊權暴力更新,新建一個集合存這些點,舊的集合存另一棵樹,在舊集合打上邊權增量標記。

時間複雜度:\(O(nlogn)\)

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

typedef long long LL;
const int mod=99990001;
const int maxn=int(2e5)+100;

struct LINK
{
    int id, pre, next;
};

int n, now;
int h[maxn], nx[maxn];
LINK t[maxn*2];
int block[maxn];
int blockcnt;
LL ans[maxn], mark[maxn];
queue<int> q[2];
int edge[2][maxn];
bool vis[maxn];

void join(int u, int v)
{
    t[now].id=v; t[now].next=h[u]; t[now].pre=-1; if (h[u]!=-1) t[h[u]].pre=now; h[u]=now++;
    t[now].id=u; t[now].next=h[v]; t[now].pre=-1; if (h[v]!=-1) t[h[v]].pre=now; h[v]=now++;
}
void read()
{
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) h[i]=-1;
    for (int i=1; i<n; ++i)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        join(u, v);
        ans[i]=w;
    }
}
void bfs(int u, int v, int blockidx, LL w)
{
    int minidx[2]={u, v};
    edge[0][0]=edge[1][0]=0;
    nx[u]=h[u]; nx[v]=h[v];

    while (!q[0].empty()) q[0].pop();
    while (!q[1].empty()) q[1].pop();
    q[0].push(u); q[1].push(v);

    while (!q[0].empty() && !q[1].empty())
    {
        for (int j=0; j<2; ++j)
        {
            int cur=q[j].front();
            bool flag=false;

            for (int i=nx[cur]; i>-1; i=nx[cur]=t[i].next)
                if (!vis[i/2+1])
                {
                    flag=true;
                    minidx[j]=min(minidx[j], t[i].id);
                    q[j].push(t[i].id);
                    edge[j][++edge[j][0]]=i/2+1;
                    nx[t[i].id]=h[t[i].id];
                    vis[i/2+1]=true;
                    nx[cur]=t[i].next;
                    break;
                }
            if (!flag) q[j].pop();
        }
    }

    int idx=0;
    if ((!q[0].empty() && q[1].empty()) || (q[0].empty() && q[1].empty() && minidx[1]<minidx[0])) idx=1;

    ++blockcnt;
    for (int i=1; i<=edge[idx][0]; ++i)
    {
        ans[edge[idx][i]]=(ans[edge[idx][i]]+mark[blockidx])*w%mod;
        block[edge[idx][i]]=blockcnt;
    }
    mark[blockidx]=(mark[blockidx]+w)%mod;

    for (int j=0; j<2; ++j)
        for (int i=1; i<=edge[j][0]; ++i) vis[edge[j][i]]=false;
}
void edge_del(int idx)
{
    for (int cur=idx*2-2; cur<idx*2; ++cur)
    {
        if (t[cur].pre!=-1) t[t[cur].pre].next=t[cur].next;
        else h[t[cur^1].id]=t[cur].next; 
        if (t[cur].next!=-1) t[t[cur].next].pre=t[cur].pre;
    }
}
void solve()
{
    blockcnt=1;
    for (int i=1; i<n; ++i) block[i]=1;

    for (int i=1; i<n; ++i)
    {
        int idx;
        scanf("%d", &idx);
        printf("%lld\n", (ans[idx]=(ans[idx]+mark[block[idx]])%mod));
        fflush(stdout);
        edge_del(idx);
        bfs(t[(idx-1)*2].id, t[idx*2-1].id, block[idx], ans[idx]);
    }
}
int main()
{
    read();
    solve();
    return 0;
}

Problem L. Preparing Problem

solution
仔細算一下就行了。

時間複雜度:\(O(1)\)

相關文章
相關標籤/搜索