Educational Codeforces Round 31 题解

比赛链接:http://codeforces.com/contest/884

A. Book Reading

题意:略

解法:水题,模拟。

#include <bits/stdc++.h>
using namespace std;
int a[110];
int main(){
    int n, t;
    scanf("%d %d", &n,&t);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    int ans = 0;
    for(int i=1; i<=n; i++){
        ans += 86400-a[i];
        if(ans>=t){
            return 0*printf("%d\n", i);
        }
    }
}

B. Japanese Crosswords Strike Back

题意:给了一个01串编码,记录1的个数放到数组里,问是否能唯一确定一个01串。

解法:判断n-1+数组的和和字符串长度是否相等就ok。

#include <bits/stdc++.h>
using namespace std;
int a[100010];
int main(){
    int n, x;
    scanf("%d %d", &n,&x);
    int sum = 0;
    for(int i=1; i<=n; i++) scanf("%d", &a[i]), sum += a[i];
    if((n-1+sum)==x){
        puts("YES");
    }else{
        puts("NO");
    }
    return 0;
}

C. Bertown Subway

题意:给了一个排列,最多可以交换一对数,问最多有多少个不同的pair(i,j)使得从i出发可以到j。

解法:随手猜一下,应该是最大的两个环合成一个环,剩下的算个组合数就好,特判只有一个环,由于是排列,直接wihle找环。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+6;
typedef long long LL;
int n, p[maxn];
bool vis[maxn];
vector <int> v;
LL cal(LL x){
    return x*(x-1)+x;
}
int main(){
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d",&p[i]);
    memset(vis, false, sizeof(vis));
    for(int i=1; i<=n; i++){
        int cnt = 0;
        if(vis[p[i]]) continue;
        int x = p[i];
        while(!vis[x]){
            ++cnt;
            vis[x]=1;
            x=p[x];
        }
        v.push_back(cnt);
    }
    sort(v.begin(),v.end());
    LL ans = 0;
    if(v.size()==1) return 0*printf("%lld\n", cal(1LL*v[0]));
    for(int i=0; i<v.size()-2; i++){
        ans += cal(1LL*v[i]);
    }
    printf("%lld\n", ans+cal(v[v.size()-1]+v[v.size()-2]));
    return 0;
}

D. Boxes And Balls

题意:有n个不同的盒子,并且有n种不同的颜色,询问最后要把第i种颜色放在i位置需要的代价,这个人每次操作分2步,第一步是把所有的没有在对应的位置的颜色拿到手里,第二步是可以放2个或者3个颜色在指定位置,问整个游戏的最小代价。

解法:样例已经提醒我们需要选最大的2个或3个,对于奇数我们直接选3个就好,对于偶数在堆里面拍一个0就转化成了第一种case。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+6;
int a[maxn];
priority_queue<LL,vector<LL>,greater<LL> >q;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    if(n%2==0) q.push(0);
    for(int i=1; i<=n; i++) q.push(a[i]);
    LL ans = 0;
    while(q.size()>1){
        LL t1=q.top(); q.pop();
        LL t2=q.top(); q.pop();
        LL t3=q.top(); q.pop();
        ans += t1+t2+t3;
        q.push(t1+t2+t3);
    }
    printf("%lld\n", ans);
    return 0;
}

E. Binary Matrix

题意:超大矩阵超小内存求联通块数量。

解法:按行扫描,保留上一行的状态以及上一行的并查集。扫描到第i行时候,把第i行值为1且相邻的元素加入到并查集中去。然后根据上一行的并查集,把第i行与上一行都为1且上下相邻的元素以上一行的元素的father点为标志,做一个等价类(等价类中包含的元素全都是第i行的)。等价类中的元素意义是他们都可以通过i-1行进行互联,然后把他们加入到并查集里面去。然后新的一行的并查集就建立完成了。把新的并查集和旧的并查集交换,这样一直做下去。

#include <bits/stdc++.h>
using namespace std;
const int maxn = (1<<14)+10;
int n, m;
char s[maxn];
short a[maxn], fa[maxn];
inline short find_set(short x){
    if(x==fa[x]) return x;
    else return fa[x]=find_set(fa[x]);
}

int main(){
    scanf("%d%d",&n,&m);
    memset(fa, -1, sizeof(fa));
    int ans = n*m;
    for(int i=0; i<n; i++){
        scanf("%s", s);
        int g = 0;
        for(int j=0; j<(m>>2); j++){
            int v = (s[j]<='9'?s[j]-'0':s[j]-'A'+10);
            for(int k=3; k>=0; --k,++g){
                a[g] = v>>k&1;
                if(a[g] == 0){
                    --ans;
                    fa[g]=-1;
                }else{
                    //先和上面的考虑联通
                    if(fa[g]!=-1){//联通成功
                        --ans;
                        int f = find_set(g);
                        fa[f] = fa[g] = g;
                    }else{
                        fa[g] = g;
                    }
                    if(g&&fa[g-1]!=-1&&find_set(g)!=find_set(g-1)){
                        --ans;
                        int fx = find_set(g);
                        int fy = find_set(g-1);
                        fa[fx] = fa[fy] = fa[g] = g;
                    }
                }
            }
        }
    }
    return 0*printf("%d\n", ans);
}

F. Anti-Palindromize

CF官方题解还提供了一个优雅的贪心解法,可以看一下。


#include <bits/stdc++.h>
using namespace std;

const int inf = 0x3FFFFFFF;
const int maxn = 2222;
struct node{
    int st, en, flow, cost, next;
    node(){}
    node(int st, int en, int flow, int cost, int next):st(st),en(en),flow(flow),cost(cost),next(next){}
}E[101000];

int num, p[maxn];
void init(){
    memset(p, -1, sizeof(p));
    num = 0;
}
void add(int st, int en, int flow, int cost){
    E[num] = node(st, en, flow, cost, p[st]);
    p[st] = num++;
    E[num] = node(en, st, 0, -cost, p[en]);
    p[en] = num++;
}
int pre[maxn];
int dis[maxn];
bool fg[maxn];
bool spfa(int st, int en)
{
    for(int i=0;i<=en;i++){
        fg[i] = 0, dis[i] = inf, pre[i]=-1;
    }
    queue<int>q;
    q.push(st);
    fg[st]=1;
    dis[st]=0;
    while(!q.empty()){
        int u = q.front(); q.pop();
        fg[u]=0;
        for(int i=p[u];~i;i=E[i].next){
            int v = E[i].en;
            if(E[i].flow&&dis[v]>dis[u]+E[i].cost){
                dis[v] = dis[u]+E[i].cost;
                pre[v]=i;
                if(!fg[v]){
                    fg[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(dis[en] < inf) return 1;
    return 0;
}

int solve(int st, int en){
    int ans=0;
    while(spfa(st,en)){
        int d = inf;
        for(int i=pre[en];i+1;i=pre[E[i].st]) d = min(d, E[i].flow);
        for(int i=pre[en];i+1;i=pre[E[i].st]){
            E[i].flow -= d;
            E[i^1].flow += d;
            ans += d*E[i].cost;
        }
    }
    return ans;
}
int n, a[111];
int cnt[30];

int main(){
    scanf("%d", &n);
    string s;
    cin >> s;
    for(int i=0; i<n; i++) cnt[s[i]-'a']++;
    for(int i=0; i<n; i++) scanf("%d",&a[i]);
    init();
    int st=n+48, en = st+1;
    for(int i=0; i<26; i++){
        add(st, i+n, cnt[i], 0);
        for(int j=0; j<n/2; j++){
            if(s[j]==s[n-1-j]){
                int cost=0;
                if(s[j]=='a'+i) cost = -max(a[j],a[n-1-j]);
                add(i+n,j,1,cost);
            }else{
                int cost=0;
                if(s[j]=='a'+i) cost=-a[j];
                if(s[n-1-j]=='a'+i) cost=-a[n-1-j];
                add(i+n,j,1,cost);
            }
        }
    }
    for(int i=0; i<n/2; i++) add(i,en,2,0);
    return 0*printf("%d\n", -solve(st,en));
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值