Educational Codeforces Round 21 题解

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

A. Lucky Year 水题

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int  main()
{
    LL n;
    scanf("%lld", &n);
    if(n<10){
        printf("1\n");
        return 0;
    }
    LL  dd = 0;
    LL  t = n;
    while(t){
        dd++;
        t/=10;
    }
   // cout<<dd<<endl;
    LL  mod = 1;
    for(LL  i=1; i<=dd; i++) mod=mod*10;
    LL  mod2 = mod/10;
    LL  low = n/mod2;
    low = (low+1)*mod2;
    LL  ans = low-n;
    printf("%lld\n", ans);
    return 0;
}

B. Average Sleep Time

题意:就是n个数,求连续的k个数相加之和的平均值。
解法:模拟一下就可以了。用前缀和求。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,k;
LL a[200010], ans, presum[200010], cnt=0;
int main()
{
    scanf("%d%d", &n,&k);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
    for(int i=1; i<=n; i++) presum[i]=presum[i-1]+a[i];
    ans = 0;

    for(int i=1; i+k-1<=n; i++){
        ans = ans+(presum[i+k-1]-presum[i-1]);
        cnt++;
    }
    printf("%.8f\n", 1.0*ans/(1.0*cnt));
    return 0;
}

C. Tea Party
题意:
Polycarp招待朋友,给朋友们倒茶,朋友们的茶杯容量不一样,Polycarp的茶壶容量是w,w小于朋友茶杯满容量的总和。倒茶时需要满足几个条件。
1.每个人的茶杯必须至少倒一半。
2.每个人的茶杯倒整数的容量。
3.如果甲茶杯的容量大于乙茶杯的容量,那么甲茶杯里的茶不能少于乙。
4.Polycarp茶壶里的茶必须倒光。

解法:首先给每个杯子倒茶(a[i]+1)/2毫升,如果茶壶水不够,输出-1.
否则,把当前茶壶剩下水,倒给其他水杯中,从容量最大的开始,直接倒满,如果还有剩余,就给第二大的水杯倒满,依次类推,直到茶壶为空。

#include <bits/stdc++.h>
using namespace std;
struct node{
    int a,b,id;
    node(int a=0,int b=0,int id=0):a(a),b(b),id(id){}
    bool operator<(const node &rhs)const{
        return a>rhs.a;
    }
}a[110];

int main(){
    int n, w, sum=0;
    scanf("%d %d", &n,&w);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i].a);
        a[i].b=(a[i].a+1)/2;
        a[i].id=a[i].a;
        sum+=(a[i].a+1)/2;
        a[i].a-=a[i].b;
    }
    if(sum>w){
        puts("-1");
        return 0;
    }
    for(int i=1; i<=n; i++) w-=a[i].b;
    for(int i=1; i<=n; i++){
        if(w){
            int mx = 0, pos = 0;
            for(int j=1; j<=n; j++){
                if(a[j].a&&mx<a[j].id){
                    mx=a[j].id;
                    pos=j;
                }
            }
            if(pos==0) break;
            else{
                int temp = min(a[pos].a, w);
                a[pos].b+=min(a[pos].a,w);
                a[pos].a-=min(a[pos].a,w);
                w-=temp;
            }
        }
    }
    if(w){
        puts("-1");
    }
    else{
        for(int i=1; i<=n; i++) printf("%d ", a[i].b);
        printf("\n");
    }
    return 0;
}

D. Array Division
题意:给出一个数组,求能否移动一个数,使得某个位置前的所有数的和是总和的一半。
解法:输入预处理下,求出前缀和,因为所有的数都是整数且互不相同,所以前缀和肯定是递增的。
对于任意一个数,如果将它放在某个数的后面使得条件满足,那么就有 sum[x] = sum/2 - b[i]; ( x>i)
如果将它放在某个数的前面使得条件满足,那么同上就有 sum[x] = sum/2 + b[i]; (x < i);

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, a[100010];
LL sum[100010];
LL sum2[100010];
int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
    for(int i=1; i<=n; i++) sum[i]=sum[i-1]+a[i], sum2[i]=sum[i];
    if(sum[n]&1){
        puts("NO");
        return 0;
    }
    sum[n]/=2;
    int flag = 0;
    for(int i=1; i<=n; i++){
        int l = lower_bound(sum2+1,sum2+i-1,sum[n]-1LL*a[i])-sum2;
        if(sum2[l]+a[i]==sum[n]&l!=i) flag = 1;
        int r = lower_bound(sum2+i+1,sum2+n,sum[n]+1LL*a[i])-sum2;
        if(sum2[r]-a[i]==sum[n]) flag=1;
    }
    if(flag) puts("YES");
    else puts("NO");
}

E. Selling Souvenirs
题意: 分别有1,2,3三种重量的物品 价值为ci 问重量为w的情况下 价值最大化其实就是增大版本的01背包
解法: 依次计算出1 2 3 背包的最大值,只有w=1 的时候 直接排序 dp[i]=dp[i-1]+c;当有重量为2的时候 比较dp[i] 之中最小的两个1相加 是否小于w=2的情况 小于直接替换;当有重量为3的时候 直接 遍历3的使用次数就可以了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, m;
priority_queue<int>q[4];
LL dp[4][300010];

int main()
{
    scanf("%d %d", &n,&m);
    for(int i=1; i<=n; i++){
        int w, c;
        scanf("%d %d", &w,&c);
        if(w==1) q[1].push(c);
        else if(w==2) q[2].push(c);
        else q[3].push(c);
    }
    int sz1 = q[1].size(), sz2 = q[2].size();
    for(int i=1; i<=min(sz1, m); i++){
        dp[1][i]=dp[1][i-1]+q[1].top();
        q[1].pop();
    }
    for(int i=sz1+1; i<=m; i++) dp[1][i]=dp[1][i-1];
    LL c, l=1, sum=0, t=0;
    for(int i=1; i<=min(sz2, m); i++){
        c = q[2].top(); q[2].pop();
        for(int j=l; j<=m; j++){
            l=j;
            if(j>=2*(t+1)&&dp[1][j-t*2]<dp[1][j-t*2-2]+c){
                dp[2][j]=dp[1][j-t*2-2]+sum+c;
                sum+=c;
                t++;
                break;
            }
            else{
                dp[2][j]=dp[1][j-t*2]+sum;
            }
        }
    }
    for(int j=l; j<=m; j++) dp[2][j]=dp[1][j-t*2]+sum;
    sum = t = 0;
    dp[3][m] = dp[2][m];
    while(q[3].size()){
        t+=3;
        sum+=q[3].top();
        q[3].pop();
        if(t<=m){
            dp[3][m]=max(dp[3][m], dp[2][m-t]+sum);
        }
    }
    printf("%lld\n", dp[3][m]);
    return 0;
}

F. Card Game

题意:有n个卡片,每个卡片有三个值:p,c,l;现在让你找一个最小的L,使得满足选出来的卡片l<=L,并且所有卡片的p的和不小于k。选择卡片时有限制,任意两张卡片的c之和不能为质数。
解法:复制官方题解。

The most tricky part of the problem is how to check if some set of cards allows us to build a deck with the required power (not taking the levels of cards into account).

Suppose we have not more than one card with magic number 1 (if there are multiple cards with this magic number, then we obviously can use only one of these). Then two cards may conflict only if one of them has an odd magic number, and another has an even magic number — otherwise their sum is even and not less than 4, so it’s not a prime number.

This allows us to solve this problem as follows:

Construct a bipartite graph: each vertex represents a card, and two vertices are connected by an edge if the corresponding pair of cards can’t be put in a deck. Then we have to find the maximum weight of independent set in this graph. This can be solved using maximum flow algorithm: construct a network where source is connected with every “odd” vertex (a vertex that represents a card with an odd magic number) by an edge with capacity equal to the power of this card; then connect every “odd” vertex to all “even” vertices that are conflicting with this vertex by edges with infinite capacities; and then connect every “even” vertex to the sink by an edge with capacity equal to the power of the card (all edges have to be directed). Then the maximum power of the deck is equal to sum - mincut, where sum is the sum of all powers and mincut is the minimum cut value between the source and the sink (which is equal to the maximum flow).

This allows us to check if we can build a deck of required power using only some set of cards (for example, only cards with level less than or equal to some x).

大概就是按照每一个c的奇偶构成了二分图,要特殊处理c=1的情况,接下来就是把所有可能的和成为素数的连边,容量为INF,然后就转化成了最大权独立集,最大权独立集=总权-最小权覆盖集,对于最小权覆盖集我们用最小割来解。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 200;
const int maxm = 30000;
const int MAXN = 2e5+5;
bool isprime[MAXN];
struct G
{
    int v, cap, next;
    G() {}
    G(int v, int cap, int next) : v(v), cap(cap), next(next) {}
} E[maxm];
int p[maxn], T, n, k;
int d[maxn], temp_p[maxn], qw[maxn]; //d顶点到源点的距离标号,temp_p当前狐优化,qw队列
void init()
{
    memset(p, -1, sizeof(p));
    T = 0;
}
void add(int u, int v, int cap)
{
    E[T] = G(v, cap, p[u]);
    p[u] = T++;
    E[T] = G(u, 0, p[v]);
    p[v] = T++;
}
bool bfs(int st, int en, int n)
{
    int i, u, v, head, tail;
    for(i = 0; i <= n; i++) d[i] = -1;
    head = tail = 0;
    d[st] = 0;
    qw[tail] = st;
    while(head <= tail)
    {
        u = qw[head++];
        for(i = p[u]; i + 1; i = E[i].next)
        {
            v = E[i].v;
            if(d[v] == -1 && E[i].cap > 0)
            {
                d[v] = d[u] + 1;
                qw[++tail] = v;
            }
        }
    }
    return (d[en] != -1);
}
int dfs(int u, int en, int f)
{
    if(u == en || f == 0) return f;
    int flow = 0, temp;
    for(; temp_p[u] + 1; temp_p[u] = E[temp_p[u]].next)
    {
        G& e = E[temp_p[u]];
        if(d[u] + 1 == d[e.v])
        {
            temp = dfs(e.v, en, min(f, e.cap));
            if(temp > 0)
            {
                e.cap -= temp;
                E[temp_p[u] ^ 1].cap += temp;
                flow += temp;
                f -= temp;
                if(f == 0)  break;
            }
        }
    }
    return flow;
}
int dinic(int st, int en, int n)
{
    int i, ans = 0;
    while(bfs(st, en, n))
    {
        for(i = 0; i <= n; i++) temp_p[i] = p[i];
        ans += dfs(st, en, inf);
    }
    return ans;
}
struct node{
    int p,c,l;
    void read(){
        scanf("%d %d %d", &p,&c,&l);
    }
}a[MAXN];
void work()
{
    for(int i=1; i<=n; i++) a[i].read();
    int s = 0, t = n+1;
    for(int i=1; i<=n; i++){//n100可以直接枚举
        int tot = 0, one = 0, p = 0;
        for(int j=1; j<=n; j++){
            if(a[j].l <= i){
                if(a[j].c == 1){
                    if(a[j].p > p){
                        one = j, p = a[j].p;
                    }
                }
                else{
                    tot += a[j].p;
                }
            }
        }
        tot += p;
        init();
        for(int j=1; j<=n; j++){
            if(a[j].l<=i&&(a[j].c>1||j==one)){
                if(a[j].c&1) add(s,j,a[j].p);
                else add(j,t,a[j].p);
            }
        }
        for(int j=1; j<=n; j++){
            if(a[j].l<=i&&(a[j].c%2||j==one)){
                for(int k=1; k<=n; k++){
                    if(a[k].l<=i&&a[k].c%2==0){
                        if(isprime[a[j].c+a[k].c]){
                            add(j, k, inf);
                        }
                    }
                }
            }
        }
        int flow = dinic(s, t, n+2);
        if(tot - flow >= k){
            printf("%d\n", i);
            return ;
        }
    }
    puts("-1");
}
int main()
{
    for(int i=2; i<=200000; i++) isprime[i]=true;
    for(int i=2; i<=200000; i++){
        if(isprime[i]){
            for(int j=i+i; j<=200000; j+=i){
                isprime[j]=false;
            }
        }
    }
    while(~scanf("%d %d", &n, &k))
    {
        work();
    }
    return 0;
}

G. Anthem of Berland

题意:给了一串,带有?,然后给了一个文本串,?可以替换成任何字符,问最后能得到的串和文本穿的最大匹配数。

解法://对t进行 KMP用类似 AC 自动机的方法预处理出t串的前i个字符加上字符j后其后缀最多能匹配t串多长的前缀。//设 dp(i,j)表示s串的前i个字符其后缀最长匹配到t串的前缀长度为j,t串的最多匹配次数

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
char s[N],t[N];
int ch[N][26], fail[N];
//对t进行 KMP用类似 AC 自动机的方法预处理出t串的前i个字符加上字符j后其后缀最多能匹配t串多长的前缀。
int main()
{
    scanf("%s", s+1);
    scanf("%s", t+1);
    int n=strlen(s+1), m = strlen(t+1);
    for(int i=1; i<=m; i++) ch[i-1][t[i]-'a']=i;
    for(int i=1, k=0; i<=m; i++){
        if(i>1){
            while(k&&t[k+1]!=t[i]) k=fail[k];
            if(t[k+1]==t[i]) fail[i]=++k;
            else fail[i]=0;
        }
        for(int j=0;j<26; j++){
            if(!ch[i][j]){
                ch[i][j]=ch[fail[i]][j];
            }
        }
    }
//    for(int i=1; i<=m; i++) printf("%d ",fail[i]);
//    for(int i=0; i<=m; i++){
//        for(int j=0; j<26; j++){
//            printf("%d ", ch[i][j]);
//        }
//        printf("\n");
//    }
    //设 dp(i,j)表示s串的前i个字符其后缀最长匹配到t串的前缀长度为j,t串的最多匹配次数
    vector <vector <int> > dp(n+1, vector<int>(m+1, -1));
    dp[0][0]=0;
    for(int i=0; i<n; i++){
        for(int j=0; j<=m; j++){
            if(dp[i][j] == -1) continue;
            for(int c=s[i+1]=='?'?0:(s[i+1]-'a'); c<(s[i+1]=='?'?26:(s[i+1]-'a'+1)); c++){
                dp[i+1][ch[j][c]] = max(dp[i+1][ch[j][c]], dp[i][j]+(ch[j][c]==m));
            }
        }
    }
    int ans=0;
    for(int i=0; i<=m; i++){
        ans = max(ans, dp[n][i]);
    }
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值