Codeforces #311 div2

2015-07-14 12:50:34

传送门

总结:前4题都不难。最后一次涉及字符串,待补...

 

 

A题、B题不说了。

 

C题:排序,枚举

  题意:有一个n个脚的桌子,每根脚有长度和拆除它的需要的能量,为了使桌子平衡,你要拆除一些脚。平衡的条件是最长的脚的数量 > k / 2(k为剩余的脚的总数量),要求求出所需的最少总能量。(n <= 10^5 ,脚的长度 <= 10^5 ,拆除脚需要的能量 <= 200)

  思路:首先将所有脚按照长度进行排序,然后枚举最后剩下的脚中的最大长度,那么显然比枚举值长的脚要拆除,因为还要使得桌子平衡,所以先统计出有多少脚的长度等于枚举的长度,设其数量为cnt,先拆除比枚举长度更长的脚,然后统计剩下的脚的数量M,那么还需要拆除的脚的数量就是ned = max(0,M-(2*cnt-1)),当然我们要找出所需能量最少的ned个脚,然后将其拆除。找的过程可以这样:由于能量的范围很小,我们可以预先统计出每个能量有多少根脚,然后动态维护这个数组,找的时候从小到大遍历即可。

#include <cstdio>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 100010;

int n;
int D[MAXN];
pii P[MAXN];

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        scanf("%d",&P[i].first);
    }
    for(int i = 1; i <= n; ++i){
        scanf("%d",&P[i].second);
        D[P[i].second]++;
    }
    sort(P + 1,P + n + 1);
    int sum = 0;
    int ans = INF;
    for(int i = n; i >= 1; ){
        int cnt = 0;
        int tmpsum = 0;
        for(int j = i; j >= 1 && P[j].first == P[i].first; --j,++cnt){
            D[P[j].second]--;
            tmpsum += P[j].second;
        }
        int ned = max(0,i - cnt * 2 + 1);
        int curcnt = 0,cursum = 0;
        for(int j = 1; j <= 200; ++j){
            if(curcnt + D[j] >= ned){
                cursum += (ned - curcnt) * j;
                ans = min(ans,sum + cursum);
                break;
            }
            else{
                curcnt += D[j];
                cursum += D[j] * j;
            }
        }
        sum += tmpsum;
        i -= cnt;
    }
    printf("%d\n",ans);
    return 0;
}    
View Code

(吐槽:一开始没看到能量的范围,当成10^5来做了,然后就用了树状数组+二分的写法,如下)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;
const int MAXN = 100010;
const long long mod = 1e8 + 7;

int n;

struct node{
    int l,d;
}p[MAXN];

struct BIT{
    int c[MAXN];
    inline int lowbit(int x){
        return x & (-x);
    }

    void update(int x,int d){
        while(x < MAXN){
            c[x] += d;
            x += lowbit(x);
        }
    }

    int getsum(int x){
        int res = 0;
        while(x){
            res += c[x];
            x -= lowbit(x);
        }
        return res;
    }
}bit1,bit2;

bool cmp(node a,node b){
    if(a.l == b.l) return a.d < b.d;
    return a.l < b.l;
}

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        scanf("%d",&p[i].l);
    }
    for(int i = 1; i <= n; ++i){
        scanf("%d",&p[i].d);
        bit1.update(p[i].d,1);
        bit2.update(p[i].d,p[i].d);
    }
    sort(p + 1,p + n + 1,cmp);
    int sum = 0,cursum = 0,m = n,curcnt = 0,ans = 1 << 30;
    for(int i = n; i >= 1; --i){
        cursum += p[i].d;
        curcnt++;
        m--;
        bit1.update(p[i].d,-1);
        bit2.update(p[i].d,-p[i].d);
        if(i == 1 || p[i].l != p[i - 1].l){ //考虑这一块
            int ned = (m + curcnt) - (curcnt * 2 - 1);
            if(ned <= 0){
                ans = min(ans,sum);
            }
            else{
                int l = 1,r = 100000;
                while(l < r){
                    int mid = (l + r) / 2;
                    if(bit1.getsum(mid) >= ned) r = mid;
                    else l = mid + 1;
                }
                int tcnt = bit1.getsum(l - 1);
                int tsum = bit2.getsum(l - 1) + (ned - tcnt) * l;
                ans = min(ans,tsum + sum);
            }
            sum += cursum;
            curcnt = cursum = 0;
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

D题:DFS(黑白染色),组合数

  题意:给出一个n个点,m条边的无向图,问你最少加多少条边使得图中出现奇环(长度>1,且为奇数),以及方案数。

  思路:这种奇偶环的问题很自然想到黑白染色,首先DFS将图黑白染色掉,然后我们发现,给同个连通块内同色的点之间连边就能构造出奇环,那么就好办了,对于每个连通块统计出黑点数cnt1和白点数cnt2,然后组合数搞下即可,方案数:C(cnt1,2) + C(cnt2,2)。

  以上是只用加一条边的情况,还有另外两种情况:(1)加两条条边,这要求每个连通块内黑白点个数均<=1,且m!=0(必须要有边存在),这时每条边都是独立的,不与其他边相邻,那么加边必定以某条边为基础,再选边端点之外的n-2个点中的一个连两条边,所以方案数:m×(n-2)

  (2)加三条边,这时要求m=0(没有边存在),方案数就是C(n,3)

#include <cstdio>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 1000010;

int n,m;
int first[MAXN],ecnt;
int vis[MAXN],col[MAXN];
int flag,cnt0,cnt1;

struct edge{
    int v,next;
}e[MAXN << 1];

inline void add_edge(int u,int v){
    e[ecnt].next = first[u];
    e[ecnt].v = v;
    first[u] = ecnt++;
}

void Dfs(int p,int c){
    if(!flag) return;
    col[p] = c;
    vis[p] = 1;
    if(c) cnt1++;
    else cnt0++;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(vis[v] == 0) Dfs(v,c ^ 1);
        else if(col[v] == c){
            flag = 0;
            return;
        }
    }
}

int main(){
    int a,b;
    memset(first,-1,sizeof(first));
    ecnt = 0;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; ++i){
        scanf("%d%d",&a,&b);
        add_edge(a,b);
        add_edge(b,a);
    }
    if(n <= 2){
        printf("0 1\n");
        return 0;
    }
    ll ans = INF,anscnt = 0;
    flag = 1;
    for(int i = 1; i <= n; ++i) if(vis[i] == 0){
        cnt0 = cnt1 = 0;
        Dfs(i,0);
        if(cnt0 >= 2){
            if(ans != 1){
                ans = 1;
                anscnt = 0;
            }
            anscnt += (ll)cnt0 * (cnt0 - 1) / 2;
        }
        if(cnt1 >= 2){
            if(ans != 1){
                ans = 1;
                anscnt = 0;
            }
            anscnt += (ll)cnt1 * (cnt1 - 1) / 2;
        }
        if(!flag) break;
    }
    if(!flag){
        printf("0 1\n");
        return 0;
    }
    if(ans == INF){
        if(m == 0){
            printf("3 %I64d\n",(ll)n * (n - 1) * (n - 2) / 6);
        }
        else{
            printf("2 %I64d\n",(ll)m * (n - 2));
        }
    }
    else{
        printf("%I64d %I64d\n",ans,anscnt);
    }
    return 0;
}
View Code

 

posted @ 2015-07-14 15:17  Naturain  阅读(96)  评论(0编辑  收藏  举报