Codeforces Round #311 (Div. 2) A,B,C,D,E

A.
Ilya and Diplomas

思路:水题了, 随随便便枚举一下,分情况讨论一下就OK了。

code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#define inf 1000000
#define clr(x, y) memset(x, y, sizeof x)

using namespace std;

int rmina,rmaxa;
int rminb,rmaxb;
int rminc,rmaxc;
int n;

int main()
{
    scanf("%d", &n);
    scanf("%d%d%d%d%d%d", &rmina, &rmaxa, &rminb, &rmaxb, &rminc, &rmaxc);
    int d = n - rminb - rminc;
    if (d > rmaxa)
    {
        d = n - rmaxa - rminc;
        if (d > rmaxb)
        {
            d = n - rmaxa - rmaxb;
            printf("%d %d %d\n", rmaxa, rmaxb, d);
        }
        else printf("%d %d %d\n", rmaxa, d, rminc);
    }
    else printf("%d %d %d\n", d, rminb, rminc);

    return 0;
}

B.
Pasha and Tea

题意:一共有w升水,有n个男生和n个女生。每个人都有一个有容量限制a[i]的杯子,每个男生分到的水容量相等,每个女生分到的水容量相等,且每个男生分到的水是女生的二倍,问所有人能分到水的容量的最大值是多少。

思路:设x为在总终状态下每个女生分到的水的量,a代表女生的最小容量,b代表男生的最小容量,则有x = min(a,b/2,w/3*n),则 ans = x * 3* n

code :

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define clr(x, y) memset(x, y, sizeof x)
#define inf 1000000000
using namespace std;

const int maxn = 300005;

int a[maxn];
int n, w;

int main()
{
    scanf("%d%d", &n, &w);
    for (int i = 0; i < 2*n; i++) scanf("%d", &a[i]);
    sort(a, a + 2*n);
    double x0 = (double)w / (3*n);
    double x1 = (double)a[0];
    double x2 = (double)a[n] / 2;

    x0 = min(x0, x1);
    x0 = min(x0, x2);

    x0 = x0 * 3 * n;

    printf("%f\n", x0);

    return 0;
}

C.
Arthur and Table

题意:给一个有n个腿的桌子,每个腿有其长度li,同时移除某个腿需要消费di的能量,当长度为最大值的腿的个数大于桌腿的总数的时候,桌子处于平稳状态,输入给出n,li,di问将桌子变成平稳状态所需要的耗费的能量的最小值。

思路:第一眼看上去肯定是要去枚举最大长度的桌腿了,但是怎么样快速的计算确定桌腿后达到平稳状态所需要花费的能量呢? 这个能量包括两部分,一部分是将长度大于当前正在枚举的桌腿的桌腿全部删掉所需要的能量,一部分是删掉使被枚举的长度的桌腿个数大于一半所要消耗的能量。 一开始我把所有的注意力全部都放在了单个的桌腿上,然后在计算复杂度的时候怎么算都感觉会超时, 后来才注意到题目的key ------- di 的大小只有200, 可以按照di对桌脚进行分类, 然后从小到大来枚举di,问题就迎刃而解了~~

code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define clr(x, y) memset(x, y, sizeof x)
#define inf 1000000000
using namespace std;

const int maxn = 100005;

int n;
int L[maxn];
int D[maxn];

int sum[maxn];
int cnt[maxn];
int cnt_d[maxn];

struct pp
{
    int li, di;

    pp(int a = 0, int b = 0) : li(a), di(b) {}

} P[maxn];

bool cmp(pp A, pp B)
{
    return A.li > B.li;
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &L[i]);
    for (int i = 0; i < n; i++) scanf("%d", &D[i]);
    for (int i = 0; i < n; i++) P[i] = pp(L[i],D[i]);

    clr(sum, 0);    clr(cnt, 0);    clr(cnt_d, 0);

    for (int i = 0; i < n; i++)
    {
        sum[L[i]] += D[i];
        cnt[L[i]] ++;
        cnt_d[D[i]] ++;
    }

    sort(P, P + n, cmp);

    int coa, ss, cnta, ans;

    coa = 0;
    cnta = 0;
    ans = inf;

    for (int i = 0; i < n; i++)
    {
        ss = coa;
        int v = P[i].li;
        int j;
        for (j = i; j < n; j++)
        {
            if (P[j].li != v) break;
            cnt_d[P[j].di]--;
        }
        i = j-1;

        int nn = n - cnta;
        int cha;
        if (cnt[v] == 1) cha = nn - 1;
        else cha = nn - (cnt[v] - 1) * 2 - 1;
        for (int i = 1; i <= 200; i++)
        {
            if (cha > cnt_d[i])
            {
                ss += cnt_d[i] * i;
                cha -= cnt_d[i];
            }
            else
            {
                if (cha >= 0) ss += i * cha;
                break;
            }
        }
        ans = min(ans, ss);
        cnta += cnt[v];
        coa += sum[v];
    }

    printf("%d\n", ans);

    return 0;
}

D. Vitaly and Cycle

题意:给出一个无向图(图可能不联通),问最少加入多少条边可以使图具有一个奇环,并计算有多少种加边的方法。

思路: 题目其实不是很难,但是自己做的时候也是磨了好久才把题目给磨出来, 做法是这样的:首先有一个大家都懂的事实:要构成奇环加入边的个数不会超过3条,然后再分情况讨论加入0,1,2,3条边的情况。

首先对图进行黑白染色,在染色的同时统计每个联通块中黑色节点的个数, 还有白色节点的个数,然后讨论

Case 0: 如果存在一个联通块黑白染色不成功,则肯定存在一个奇环,此时所需要加入的边数就为0,其方案数固定为1。(如果有一条边的两端颜色是一样的则染色不成功)

Case 1: 对于一个联通块,如果用一条边将两个黑色点或者白色点连起来,就能形成一个奇环,所以就可以通过一开始所统计的每个联通块中黑白节点的个数快速的计算出来。

Case 2: 如果每个联通块都是1个顶点或者两个顶点,则用2条边就能形成环,这种就统计一下一个点的连通块和两个点的连通块各有多少个再用组合数学搞一搞就行了。

Case 3:   全部是独立点的情况, 从中随意取三个点就可以了~~~

ps: 黑白染色其实直接跑一遍dfs(or bfs) 就行了, 我太年轻的先跑了一个生成树,然后则染色,其实没有必要~。

code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define clr(x, y) memset(x, y, sizeof x)
#define inf 1000000000
using namespace std;

const int maxn = 200005;

vector<int> G[maxn];

struct edge
{
    int st,ed;
    bool flag;
} E[maxn];

int n, m;
int par[maxn];
int col[maxn];
int white[maxn];
int black[maxn];
int ta;

void init(int n_)
{
    for(int i = 0; i <= n_; i++) par[i] = i;
}

int Find(int x)
{
    if (x == par[x]) return x;
    return par[x] = Find(par[x]);
}
void unit(int x, int y)
{
    x = Find(x);
    y = Find(y);
    if (x == y) return ;
    par[y] = x;
}
bool same(int x, int y)
{
    return Find(x) == Find(y);
}

void dfs(int u, int &wh, int &bl)
{
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if (col[v] != -1) continue;
        col[v] = col[u]^1;
        col[v] == 0? wh++ : bl++;
        dfs(v, wh, bl);
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    int st, ed;
    for (int i = 0; i < m; i++)
    {
        scanf("%d%d", &E[i].st, &E[i].ed);
        E[i].flag = false;
    }
    init(n);
    for (int i = 0; i < m; i++)
    {
        if (!same(E[i].st, E[i].ed))
        {
            unit(E[i].st, E[i].ed);
            G[E[i].st].push_back(E[i].ed);
            G[E[i].ed].push_back(E[i].st);
            E[i].flag = true;
        }
    }
    clr(col, -1);   clr(white, 0);  clr(black, 0);  ta = 0;
    for (int i = 1; i <= n; i++)
    {
        if (col[i] == -1)
        {
            int wh = 1;
            int bl = 0;
            col[i] = 0;
            dfs(i, wh, bl);
            white[ta] = wh;
            black[ta++] = bl;
        }
    }
    for (int i = 0; i < m; i++)
    {
        if (E[i].flag == true) continue;
        if (col[E[i].st] == col[E[i].ed])
        {
            printf("0 1\n");
            return 0;
        }
    }
    long long w = 0;
    for (int i = 0; i < ta; i++)
    {
        if (white[i] > 1) w += (long long)white[i]*(white[i]-1)/2;
        if (black[i] > 1) w += (long long)black[i]*(black[i]-1)/2;
    }
    if (w > 0)
    {
        printf("1 %lld\n", w);
        return 0;
    }
    int cnt = 0;
    int tmp = n;
    for (int i = 0; i < ta; i++)
    {
        if (white[i]+black[i] > 1) cnt++;
    }
    for (int i = 0; i < ta; i++)
    {
        if (white[i]+black[i] > 1)
        {
            cnt--;
            tmp -= 2;
            w += tmp;
            w += 2*cnt;
        }
        else
        {
            tmp --;
            w += cnt;
        }
    }
    if (w > 0)
    {
        printf("2 %lld\n", w);
        return 0;
    }
    w += (long long)n*(n-1)*(n-2)/6;
    printf("3 %lld\n", w);
    return 0;

}

E. Ann and Half-Palindrome

题意: 首先题目定义了一个半回文串:一个串是半回文串,当且仅当,这个中间轴左边的奇数位置上的字符与其对称位置上的字符相同,题目给出一个串s,和整数k让你求出s的所有半回文子串中字典序第k大的串,s的长度是5000。

思路:这道题目做起来也可谓是波折满满啊, 一开始听刘神说是暴力,就自己敲了一个最最暴力的做法,尼玛啊O(n^3)的方法你怕不怕,然后一算肯定超时啊,就在想怎么来优化,感觉枚举子串的地方没有什么可优化的了, 就在想判回文的地方有没有什么可以更快的方法,没想出来(还是太弱了),然后刘神告诉我说用区间dp的思想随便预处理一下就行了,顿时恍然大悟,马上又去改了一发,发现超内存 ->___->,然后刘神有告诉我说用动态字典树啊,然后我就去改了字典树,发现T了~~~~, 一看刘神的姿势才发现字典树的插入是O(n)的,但是他没插入一个就相当于插入了所有的以插入串开头的前缀子串,所以在枚举子串的时候只有枚举头就行了~~~,又改了一发最后才过...

其中字典树的每个节点都多维护一个域用来存,其子树中有多少个半回文串,之后找第K大的串就用类似平衡树上dfs查找某个节点的思想不断的查找左右子树就行了,其中还有一些小的细节需要注意~~~

code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define clr(x, y) memset(x, y, sizeof x)
#define inf 1000000000
using namespace std;

string s;
int k;
vector<string> vv;

int dp[5005][5005];

int dfs(int i, int j)
{
    if (i == j) return dp[i][j] = 1;
    if (j < i) return dp[i][j] = 1;

    if (dp[i][j] != -1) return dp[i][j];
    if (dfs(i+2,j-2) && s[i] == s[j]) return dp[i][j] = 1;
    return dp[i][j] = 0;
}

class Trie
{
public:
    int flag;
    int num;
    Trie* next[2];
    Trie()
    {
        flag=0;
        num=0;
        next[0] = next[1] = 0;
    }
} * root;

int dfs(Trie * p)
{
    if (!p) return 0;
    p->num += p->flag;
    p->num += dfs(p->next[0]);
    p->num += dfs(p->next[1]);
    return p->num;
}
string solve(Trie * p, int k)
{
    Trie * lson = p->next[0], *rson = p->next[1];;
    int lnum, rnum;
    if (lson == 0) lnum = 0;
    else lnum = lson->num;
    if (rson == 0) rnum = 0;
    else rnum = rson->num;

    if (p->flag >= k) return "";
    else if (lnum + p->flag >= k) return "a" + solve(lson,k-p->flag);
    else return "b" + solve(rson, k - lnum - p->flag);
}

void Trie_insert(int st, int ed)
{
    Trie* p= root;
    for(int i = st; i < ed; i++)
    {
        int index;
        if (s[i] == 'a') index = 0;
        else index = 1;

        if(!p->next[index]){
            p->next[index]=new Trie;
        }
        p=p->next[index];
        if (dfs(st,i)) p->flag++;
    }
}

int main()
{
    cin>>s>>k;
    int len = s.length();
    clr(dp, -1);
    root = new Trie();
    for (int i = 0; i < len; i++)
    {
        Trie_insert(i, len);
    }
    dfs(root);
    cout<< solve(root,k) <<endl;

    return 0;
}

补完了一场题目,给自己加个油~~

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2025-01-31 04:06:25

Codeforces Round #311 (Div. 2) A,B,C,D,E的相关文章

Codeforces Round #311 (Div. 2)

D 分4种情况讨论 1 不需要加边 , 就是说原本就有一个奇数环,我们只要跑一次二分图就好了 2 加一条边 , 也就是说存在大于等于3个点的联通块 我们对于这个联通块也跑一遍二分图, 可以知道图中所有的 同颜色染色中的点任意相连,都是一个奇数环,那么对于每个联通分量都有相应的数可以计算 3 加两条边,也就是说没有大于两个点的联通块存在,并且有两个点的联通块存在,答案也是可以计算出来的 e*(n-2) 4 加三条边 那么就可以知道每个联通块只有一个点,答案是 n*(n-1)*(n-2)/6; #i

Codeforces Round #311 (Div. 2) E. Ann and Half-Palindrome (DP+字典树)

题目地址:传送门 先用dp求出所有的符合要求的半回文串,标记出来.然后构造字典树.然后再dfs一遍求出所有节点的子树和,最后搜一遍就能找出第k个来了. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map>

Codeforces Round #311 (Div. 2)--D(图

题意:给出一个图,问最少加多少条边能连出一个奇圈,还要求输出连边的方法种数. 思路:比赛的时候看了一下是图相关的就没多想,也没时间了,其实挺简单的,如果已经有奇圈,那么直接输出0 1,如果最多有两个点相连,那么就是(n-2)×m,就是每条边和另外的任意点连两条边,要么就是没有边,这个就是直接输出结果.. 稍微麻烦一点的是最后一种,联通块有多个点且没有奇圈,这时候需要把联通快黑白染色,同色的点相连就是一个奇圈,dfs的时候统计每个联通块中两种=颜色的个数即可. #include<bits/stdc

Codeforces Round #311 (Div. 2)A Ilya and Diplomas

[比赛链接]click here~~ [题目大意] n个人,获取一到三等文凭,每门文凭有大小范围,求最后文凭各颁发多少 [解题思路]直接枚举了, 看完题,赶紧写了一发代码,发现居然错过注册时间,系统提示不能提交代码,真是醉了~~,以后还是得提前注册: A题,比较简单: 代码: #include <iostream> #include <bits/stdc++.h> using namespace std; int main() { int n,m; int a1,a2; int b

Codeforces Round #311 (Div. 2) D. Vitaly and Cycle(二分图染色,奇环)

给定n个点,m条边. 求最少最要加几条边使图中存在奇环,且输出此时加边的方法种数 根据题意,只可能为 0:已经存在奇环,dfs搜到已经染色的且颜色相同 1:判断每个连通块里的 染色黑白色的个数 2 :某个点的 度 > 1 3:0条边 1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include<vector> 5 #include<stack> 6 #include

2017-4-25-Train:Codeforces Round #311 (Div. 2)

A. Ilya and Diplomas(贪心) Soon a school Olympiad in Informatics will be held in Berland, n schoolchildren will participate there. At a meeting of the jury of the Olympiad it was decided that each of the n participants, depending on the results, will g

Codeforces Round #311 (Div. 2) B. Pasha and Tea

[题目链接]click here~~ [题目大意]给你n个boy,n个girl ,然后W表示茶壶的最大容量,然后n个茶杯,每个都有不同的容量,要求boy的茶杯里的茶水是girl的两倍,且boy和boy容量一样,girl和girl 容量一样,问如何倒茶,最大化总的茶量 [解题思路]这道题本来很简单,第一次读题没读明白,以为每个茶杯的茶水都倒满了,然后一想不就是拿最大的计算吗.一交,直接WA,然后仔细读题,发现,每个茶杯的茶水可以选择的倒(坑~啊),那么既然boy和boy,girl和girl的茶水一

Codeforces Round #279 (Div. 2) ABCD

Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems # Name     A Team Olympiad standard input/output 1 s, 256 MB  x2377 B Queue standard input/output 2 s, 256 MB  x1250 C Hacking Cypher standard input/output 1 s, 256 MB  x740 D Chocolate standard input/

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i