1. 程式人生 > 實用技巧 >COCI2014-2015 Contest#1 題目選做

COCI2014-2015 Contest#1 題目選做

COCI2014-2015 Contest#1 題目選做

A.PROSJEK

Description

有一個數列 a,現在按照下列公式求出一個數列 $b_i = \frac{{}\sum_{j = 1}^{i}a_i}{i} $

給你數列 b,請求出數列 a

Solution

顯然通過b預處理出字首和直接搞定

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        
if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 100 + 10; long long sum[MAXN]; int main() { int n = read(); for
(int i=1;i<=n;i++) { int x = read(); sum[i] = 1LL * x * i; } for(int i=1;i<=n;i++) printf("%lld ",sum[i] - sum[i-1]); }
View Code

B.KLOPKA

Description

在平面直角座標系上有 n 個點。

現在要用一個正方形將點框起來,使得每一個點都能在正方形的內部或邊上。要求這個正方形的邊平行於座標軸。

求出這個正方形的最小面積。

Solution

記錄一下橫縱座標最大最小值即可

#include<bits/stdc++.h>

using
namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 20 + 10; int n; int x[MAXN],y[MAXN]; int maxx,maxy,minx,miny; int main() { n = read(); minx = 1<<30,miny = 1<<30; for(int i=1;i<=n;i++) { x[i] = read(),y[i] = read(); maxx = max(maxx,x[i]); maxy = max(maxy,y[i]); minx = min(minx,x[i]); miny = min(miny,y[i]); } cout << max((maxx - minx)*(maxx - minx),(maxy - miny) * (maxy - miny)) << endl; }
View Code

D.MAFIJA

Description

n 個人,其中有一些人是平民,有一些人是壞蛋。

現在,平民們想揪出所有的壞蛋,於是 n 個人都指認了一個人是壞蛋。

如果一個人是平民,他會隨便亂指認,否則,他會指認一個平民。

求出最多的壞蛋個數。

Solution

把指認的關係當做邊連邊,把壞蛋當做染成1,好人當做染成0

由於壞蛋不能連壞蛋,與1相連的點一定為0

假如是一條鏈的話,顯然交叉染色,從鏈尾開始染更優

把鏈擴充套件成樹,發現每次把入度為0的點染成1一定最優

但是實際上有可能出現環

考慮為環的情況,如果環上的點都沒有限制,顯然隨便選一個點染成0,這個點兩邊染色就沒有限制了,就斷成鏈了

考慮環上的點有限制的情況,即為基環樹的情況

還是從入度為0的點開始染,環上有點被確定染成了0那麼這個環顯然就被斷成鏈了,就按找鏈的方式貪心即可

所以可以設計出這樣一個貪心演算法

每次把入度為0且沒有確定下來的點染成1,這樣最後剩下的一定是幾個獨立的環

每次隨便選環上一點染成0,把環斷成鏈即可

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'||ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 5e5 + 10;

int n;
int a[MAXN];
int deg[MAXN]; 
bool vis[MAXN];
int ans;

inline void dfs(int x,int col)
{
    if(vis[x]) return;
    vis[x] = 1; 
    ans += col;
    deg[a[x]]--;
    if((!deg[a[x]])||col == 1) dfs(a[x],col^1);
}

int main()
{
    n = read();
    for(int i=1;i<=n;i++) a[i] = read(),deg[a[i]]++;
    for(int i=1;i<=n;i++) if(!deg[i]) dfs(i,1);
    for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
    cout << ans << endl;
}
View Code

E.ZABAVA

Description

一座新的公寓開放了,這座公寓有 m 棟樓。

現在會有 n 個學生,每一天都會進來一個人。

一棟樓進來一個人後,會進行一場派對,派對會有與當前人數相等的吵鬧指數。

但是,現在可以進行 k 次操作,每一次操作可以將一棟樓裡的全部學生踢出這座新公寓。

請注意,學生先進樓,然後才能進行操作。

現在求出最小吵鬧指數的相加之和。

你不必使用完全部的操作。

Solution

注意到每座公寓是獨立的

每個公寓對答案的貢獻只和分了幾次有關

這就是揹包問題

考慮如何計算每個公寓分X次的最小吵鬧和

容易發現人數是一定,要最小化答案一定是儘量均分

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'||ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 500 + 10;

long long n,m,k;
long long p[MAXN];
long long f[MAXN][MAXN];

inline long long calc(long long x,long long K)
{
    long long val = x/K;
    long long num1 = (val+1) * K - x;
    long long  num2 = K - num1;
    return num2 * (val + 1) * (val + 2)/2 + num1 * val * (val + 1)/2;
}

int main()
{
    n = read(),m = read(),k = read();
    for(int i=1;i<=n;i++)
    {
        int x = read();
        p[x]++;
    }
    memset(f,0x3f,sizeof(f));
    for(int j=0;j<=k;j++) f[0][j] = 0;
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<=k;j++)
        {
            for(int h=0;h<=j;h++)
            {
                int l = j - (h);
                f[i][j] = min(f[i][j],f[i-1][h] + calc(p[i],l+1));
            }
        }
    }
    cout << f[m][k] << endl;
}
View Code

F.Kamp

Description

一顆樹 n 個點,n−1 條邊,經過每條邊都要花費一定的時間,任意兩個點都是聯通的。

K 個人(分佈在 K 個不同的點)要集中到一個點舉行聚會。

聚會結束後需要一輛車從舉行聚會的這點出發,把這 K 個人分別送回去。

請你回答,對於 i=1∼n,如果在第 i個點舉行聚會,司機最少需要多少時間把 K 個人都送回家

Solution

很簡單的換根DP

考慮到以X為根的答案,相當於X的子樹中的答案,以及它到子樹外的答案減掉可以省下的最大距離

就維護一下就沒了

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'||ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 500000 + 10;

int n,k;
vector<pair<int,int> >G[MAXN];
int a[MAXN],sz[MAXN];
long long f[MAXN],g[MAXN],h[MAXN];
long long dis1[MAXN],dis2[MAXN];

inline void dfs(int x,int ff)
{
    sz[x] += a[x];
    for(int i=0;i<G[x].size();i++)
    {
        int v = G[x][i].first;
        if(v == ff) continue;
        dfs(v,x);
        sz[x] += sz[v];
        if(sz[v])
        {
            f[x] += (f[v] +  2 * (G[x][i].second));
            long long w = G[x][i].second;
            if(dis1[x] < dis1[v] + w) 
            {
                dis2[x] = dis1[x];
                dis1[x] = dis1[v] + w; 
            }
            else if(dis2[x] < dis1[v] + w) dis2[x] = dis1[v] + w;
        }
    }
}

inline void dp(int x,int ff)
{
    for(int i=0;i<G[x].size();i++)
    {
        int v = G[x][i].first;
        if(v == ff) continue;
        if(sz[v] == k)
        {
            dp(v,x);
            continue;
        }
        g[v] = g[x] + (f[x] - f[v]);
        long long w = G[x][i].second;
        if(!sz[v]) g[v] += 2 * ( G[x][i].second);
        if(dis1[v]+w == dis1[x]) h[v] = max(h[x] , dis2[x]) + w;
        else h[v] = max(h[x],dis1[x]) + w;
        dp(v,x);
    }
}

int main()
{
    n  = read(),k = read();
    for(int i=1;i<n;i++)
    {
        int a = read(),b = read(),c = read();
        G[a].push_back(make_pair(b,c));
        G[b].push_back(make_pair(a,c)); 
    }
    for(int i=1;i<=k;i++) a[read()]++;
    dfs(1,0);
    //cout << 1 << endl;
    dp(1,0);
    for(int i=1;i<=n;i++) printf("%lld\n",f[i] + g[i] - max(dis1[i] , h[i]));
}
View Code