Codeforces Round #568 (Div. 2)简要题解

A. Ropewalkers

分析:固定中间点,移动两边的点就可以了。

#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
    int n;vector<int> v;
    BIT(int n) : n(n) { v.resize(n + 1); }
    void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
    long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
int n;
long long a[10];
int main(){
    long long d;
    cin>>a[0]>>a[1]>>a[2]>>d;
    sort(a,a+3);
    long long ans = 0;
    if(a[1]-a[0]<d)ans += d - (a[1]-a[0]);
    if(a[2]-a[1]<d)ans += d - (a[2]-a[1]);
    cout<<ans<<endl;
}

B. Email from Polycarp

分析:双下标跑一遍就可以了。

#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
    int n;vector<int> v;
    BIT(int n) : n(n) { v.resize(n + 1); }
    void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
    long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
int n;
long long a[10];
int main(){
    cin>>n;
    while(n--){
        string s,t;
        cin>>s>>t;
        int pos = 0;
        bool ok = 1;
        for (int i = 0; i < t.length(); ++i) {
            if(t[i]==s[pos]){
                pos++;
            }
            else
            {
                if(i>0&&t[i]==t[i-1])continue;
                else ok=0;
            }
        }
        if(pos != s.length())ok=0;
        if(ok)puts("YES");
        else puts("NO");
    }
}

C1. Exam in BerSU (easy version)&&C2. Exam in BerSU (hard version)

分析:维护一个sum代表到第i个位置所有的时间总和,然后在其由大到小的排列中,找出一个最小前缀和使得sum - m <= pre

直接用树状数组维护,插入的时候在t[i]位置add t[i],然后二分找前缀和,由于这个位置可能加了多次,特殊判断一下就可以了。

然而这题t[i]只有100,所以直接计数排序,然后查询的时候遍历也是可以的。(提前打的bit板子果然用上了)

#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
    int n;vector<int> v;
    BIT(int n) : n(n) { v.resize(n + 1); }
    void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
    long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
BIT c(100),cc(100);
int n,m;
int t[200004];
int times[104];
multiset<int>s;
int main(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        scanf("%d",&t[i]);
    }
    int sum=0;
    memset(times,0, sizeof(times));
    for (int i = 1; i <= n; ++i) {
        sum+=t[i];
        if(sum>m){
            int left = sum - m;
            int l = 1,r = 100;
            int res = 0,ans = 0;
            while(l<=r){
                int mid = (l+r)>>1;
                if(c.que(100)-c.que(mid-1)>=left){
                    res = mid;
                    l=mid+1;
                    ans = cc.que(100)-cc.que(mid-1);
                }
                else r= mid-1;
            }
            int duo = c.que(100)-c.que(res-1) - left;
            int nu = duo/res;
            if(times[res]>nu)ans-=nu;
            printf("%d ",ans);
        }
        else printf("0 ");
        c.update(t[i],t[i]);
        cc.update(t[i],1);
        times[t[i]]++;
    }
}

D. Extra Element

分析:排序之后差分得到数组b[i],用map记录一下b[i]出现的次数。

当删除第i个数的时候,要维护新的差分数组,只需要将这个数的两个差分值加起来(因为事先排序了)然后再然进去,所以可以直接枚举删除的点,然后用map维护一下,如果某次删除后map中只有一个元素,那么就是答案了。(当map值减到0时要erase掉)

#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
    int n;vector<int> v;
    BIT(int n) : n(n) { v.resize(n + 1); }
    void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
    long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
BIT c(100),cc(100);
int n,m;
struct node
{
    int w,id;
    bool friend operator < (node a,node b){
        return a.w<b.w;
    }
}a[200004];
int b[200004];
int main(){
    cin>>n;
    for (int i = 1; i <= n; ++i) {
        scanf("%d",&a[i].w);
        a[i].id=i;
    }
    if(n==2||n==3){
        printf("1\n");
        return 0;
    }
    sort(a+1,a+1+n);
    for (int i = 2; i <= n; ++i) {
        b[i]=a[i].w-a[i-1].w;
    }
    unordered_map<int,int>mp;
    int d=-1;
    for (int i = 2; i <= n; ++i) {
        mp[b[i]]++;
    }
    if(mp.size()>3){
        puts("-1");
        return 0;
    }
    int ans = -1;
    for (int i = 1; i <= n; ++i) {
        if(i==1){
            mp[b[i+1]]--;
            if(mp[b[i+1]]==0)mp.erase(b[i+1]);
            if(mp.size()==1){
                ans = i;
                break;
            }
            mp[b[i+1]]++;
        }
        else if(i==n){
            mp[b[i]]--;
            if(mp[b[i]]==0)mp.erase(b[i]);
            if(mp.size()==1){
                ans = i;
                break;
            }
            mp[b[i]]++;
        }
        else
        {
            mp[b[i+1]]--;
            mp[b[i]]--;
            if(mp[b[i]]==0)mp.erase(b[i]);
            if(mp[b[i+1]]==0)mp.erase(b[i+1]);
            mp[b[i]+b[i+1]]++;
            if(mp.size()==1){
                ans = i;
                break;
            }
            mp[b[i+1]]++;
            mp[b[i]]++;
            mp[b[i]+b[i+1]]--;
            if(mp[b[i]+b[i+1]]==0)mp.erase(b[i]+b[i+1]);
        }
    }
    if(ans==-1)puts("-1");
    else
    cout<<a[ans].id<<endl;
}

E. Polycarp and snakes

分析:比赛时我以为蛇可以是折线。。怀疑人生。

因为蛇只能是直线,所以直接找对应字母第一次出现的地方和最后一次出现的地方,然后跑一个直线就可以了。

注意如果在途中遇见了‘.’或者比自己小的字母,就是NO,或者这条路径没有完全包含所有的该字母,也是NO。

#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
    int n;vector<int> v;
    BIT(int n) : n(n) { v.resize(n + 1); }
    void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
    long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
int n,m;
char a[2004][2004];
int ans[30][2][2];
int num[30];
struct node
{
    int x,y;
};
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>m;
        for (int i = 1; i <= n; ++i) {
            scanf("%s",a[i]+1);
        }
        int maxn = 0;
        memset(ans,0, sizeof(ans));
        memset(num,0, sizeof(num));
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                if(a[i][j]<='z' && a[i][j]>='a'){
                    num[a[i][j]-'a']++;
                    if(ans[a[i][j]-'a'+1][0][0]==0){
                        ans[a[i][j]-'a'+1][0][0]=i;
                        ans[a[i][j]-'a'+1][0][1]=j;
                    }
                    ans[a[i][j]-'a'+1][1][0]=i;
                    ans[a[i][j]-'a'+1][1][1]=j;
                    maxn = max(maxn,a[i][j]-'a'+1);
                }
            }
        }
        if(maxn==0){
            puts("YES");
            puts("0");
            continue;
        }
        bool ok = 1;
        for (int i = 1; i <= maxn && ok; ++i) {
            if(ans[i][0][0]==0){
                ans[i][0][0]=ans[maxn][0][0];
                ans[i][0][1]=ans[maxn][0][1];
                ans[i][1][0]=ans[maxn][0][0];
                ans[i][1][1]=ans[maxn][0][1];
                continue;
            }
            if(ans[i][0][0]!=ans[i][1][0] && ans[i][0][1]!=ans[i][1][1])ok=0;
            else
            {
                int cnt = 0;
                if(ans[i][0][0]==ans[i][1][0]){
                    for (int j = ans[i][0][1]; j <= ans[i][1][1] && ok; ++j) {
                        if(a[ans[i][0][0]][j]=='.' || a[ans[i][0][0]][j]<i+'a'-1)ok=0;
                        if(a[ans[i][0][0]][j]==i+'a'-1)cnt++;
                    }
                }else {
                    for (int j = ans[i][0][0]; j <= ans[i][1][0] && ok; ++j) {
                        if(a[j][ans[i][0][1]]=='.' || a[j][ans[i][0][1]]<i+'a'-1)ok=0;
                        if(a[j][ans[i][0][1]]==i+'a'-1)cnt++;
                    }
                }
                if(cnt != num[i-1])ok=0;
            }
        }
        if(ok){
            puts("YES");
            printf("%d\n",maxn);
            for (int i = 1; i <= maxn; ++i) {
                printf("%d %d %d %d\n",ans[i][0][0],ans[i][0][1],ans[i][1][0],ans[i][1][1]);
            }
        }
        else puts("NO");
    }
}

F. Two Pizzas

分析:因为E怀疑人生之后就直接去看剩下过的最多的G1了,赛后发现这题还是比较简单的?

虽然给的数据有1e5个,但是对应的状态只有2^9个,记录每个状态对应的pizza的最小值,然后暴力跑一个两个pizza的组合就可以找出每个状态所需的最小花费。然后枚举512个状态,每种状态对应每个人,如果可以满足这个人,有sra & p == p,sta代表枚举的状态,p代表这个人的需求,所以直接暴力跑就可以了 。

#include "bits/stdc++.h"
using namespace std;
int f[100004];
int like[100004];
long long c[100004];
int r[100004];
int pz[100004];
struct node
{
    long long c;
    int id;
    bool friend operator < (node a,node b){
        if(a.c==b.c)return a.id<b.id;
        return a.c<b.c;
    }
};
multiset<node>a[1120];
long long b[1120];
int posb[1120][2];
int main(){
    int n,m;
    memset(like,0, sizeof(like));
    for (int i = 0; i < (1<<9); ++i) {
        b[i]=8e18;
    }
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        scanf("%d",&f[i]);
        for (int j = 0; j < f[i]; ++j) {
            int x;scanf("%d",&x);
            x--;
            like[i] |= (1<<x);
        }

    }
    for (int i = 1; i <= m; ++i) {
        scanf("%lld%d",&c[i],&r[i]);
        for (int j = 0; j < r[i]; ++j) {
            int x;scanf("%d",&x);
            x--;
            pz[i] |= (1<<x);
        }
        a[pz[i]].insert({c[i],i});
    }
    for (int i = 0; i < (1<<9); ++i) {
        for (int j = i; j < (1<<9); ++j) {
            if(j==i && a[i].size()>=2){
                int cost = a[i].begin()->c + (++a[i].begin())->c;
                if(cost < b[i]){
                    b[i]=cost;
                    posb[i][0]=a[i].begin()->id;
                    posb[i][1]=(++a[i].begin())->id;
                }
            }
            else if(j!=i){
                if(!a[i].size() || !a[j].size())continue;
                int cost = a[i].begin()->c + a[j].begin()->c;
                if(cost < b[j|i]){
                    b[i|j] = cost ;
                    posb[i|j][0]=a[i].begin()->id;
                    posb[i|j][1]=a[j].begin()->id;
                }
            }
        }
    }
    int maxi = 0;
    long long mini = 8e18;
    int ans=-1;
    for (int bit = 0; bit < (1<<9); ++bit) {
        int cnt = 0;
        for (int i = 1; i <= n; ++i) {
            int temp = bit & like[i];
            if(temp == like[i])cnt++;
        }
        if(cnt > maxi && b[bit]!=8e18){
            ans = bit;
            maxi = cnt;
            mini = b[bit];
        }
        else if(cnt == maxi && mini > b[bit]){
            ans = bit;
            maxi = cnt;
            mini = b[bit];
        }
    }
    if(ans==-1)printf("1 2\n");
    else
   // printf("%d %d \n",maxi,mini);
    printf("%d %d\n",posb[ans][0],posb[ans][1]);
}

 G1. Playlist for Polycarp (easy version)

分析:这题其实感觉我是卡过去的。。。

对15首歌曲进行状压,DP[i][j][k]代表时间总长为i,以第j种歌曲结尾,k表示选取状态的方案数。

dp[i+len[k]][g[k]][j|x]+=dp[i][l][j]

初始化

dp[0][1][0]=1;
dp[0][2][0]=1;
dp[0][3][0]=1;

因为在放第一首的时候,如果这首歌的种类是1,那么这个dp方程是算了以2,3结尾的,所以这里算了两次,最后答案加起来除个2就可以了。

然后这里dp数组稍微开大一点就MLE了。。果然是玄学过题

#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
int len[16],g[16];
int dp[226][4][1<<15+1];
int main(){
    int n,T;
    cin>>n>>T;
    for (int i = 0; i < n; ++i) {
        scanf("%d%d",&len[i],&g[i]);
    }
    memset(dp,0, sizeof(dp));
    dp[0][3][0]=1;
    dp[0][1][0]=1;
    dp[0][2][0]=1;
    for (int i = 0; i < T; ++i) {
        for (int j = 0; j < (1<<n); ++j) {
            for (int k = 0; k < n; ++k) {
                if(i+len[k]>T)continue;
                int x = (1<<k);
                if(j&x)continue;
                for (int l = 1; l <= 3; ++l) {
                    if(dp[i][l][j]==0)continue;
                    if(l==g[k])continue;
                    dp[i+len[k]][g[k]][j|x]+=dp[i][l][j];
                    dp[i+len[k]][g[k]][j|x]%=mod;
                }
            }
        }
    }
    long long ans = 0;
    for (int i = 1; i <= 3; ++i) {
        for (int j = 0; j < (1<<n); ++j) {
            ans += dp[T][i][j];
            ans %= mod;
        }
    }
    cout<<ans*qk(2,mod-2)%mod<<endl;
}

G2. Playlist for Polycarp (hard version)

更好的解法还没想到,留坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值