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

 

posted @ 2020-10-23 15:18  wlzs1432  阅读(280)  评论(0编辑  收藏  举报