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

A. Ilya and Diplomasios

思路:水題了, 隨隨便便枚舉一下,分狀況討論一下就OK了。post


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 Teaspa


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


思路:設x爲在總終狀態下每個女生分到的水的量。a表明女生的最小容量,b表明男生的最小容量。則有x = min(a,b/2,w/3*n)。則 ans = x * 3* nip


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 Tableci


題意:給一個有n個腿的桌子,每個腿有其長度li,同一時候移除某個腿需要消費di的能量,當長度爲最大值的腿的個數大於桌腿的總數的時候,桌子處於平穩狀態,輸入給出n,li,di問將桌子變成平穩狀態所需要的耗費的能量的最小值。get


思路:第一眼看上去確定是要去枚舉最大長度的桌腿了,但是怎麼樣高速的計算肯定桌腿後達到平穩狀態所需要花費的能量呢? 這個能量包含兩部分,一部分是將長度大於當前正在枚舉的桌腿的桌腿全部刪掉所需要的能量,一部分是刪掉使被枚舉的長度的桌腿個數大於一半所要消耗的能量。 一開始我把全部的注意力全部都放在了單個的桌腿上,而後在計算複雜度的時候怎麼算都感受會超時。 後來才注意到題目的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;
}


題意:給出一個無向圖(圖可能不聯通)。問最少增長多少條邊可以使圖具備一個奇環,並計算有多少種加邊的方法。


思路: 題目事實上不是很是難。但是本身作的時候也是磨了很久才把題目給磨出來, 作法是這種:首先有一個你們都懂的事實:要構成奇環增長邊的個數不會超過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;
}


補完了一場題目。給本身加個油~~

相關文章
相關標籤/搜索