Codeforces CROC 2016 - Elimination Round

C. Enduring Exodus

从左到右枚举人住的房间,牛住的房间不断往后推。

#include <cstdio>
#include <iostream>
#include <algorithm>


using namespace std;

char str[100010];

int Next[100010];

int calc(int pos,int l,int r){
    return max(r-pos,pos-l);
}

int main(){
    int n,k;
    cin>>n>>k;
    scanf("%s",str);
    for(int i=n-2;i>=0;i--){
        if(str[i+1]=='0'){
            Next[i]=i+1;
        }else{
            Next[i]=Next[i+1];
        }
    }
    int pos = 0;
    while(str[pos]!='0'){
        pos++;
    }
    int l = pos;
    int r = pos+1;
    int cnt = 0;
    while(1){
        if(str[r]=='0'){
            cnt++;
        }
        if(cnt==k)break;
        r++;
    }
    int ans = calc(pos,l,r);

    for(int i=Next[pos];i!=0;i=Next[i]){
        if(i>r){
            r=Next[r];
            l=Next[l];
        }
        if(r==0)break;
        ans = min(ans,max(r-i,i-l));
        while(i-l>r-i){
            if(r==0)break;
            ans = min(ans,max(r-i,i-l));
            r=Next[r];
            l=Next[l];
            if(r==0)break;
            ans = min(ans,max(r-i,i-l));
        }
        if(r==0)break;
    }
    cout<<ans<<endl;
    return 0;
} 

D. Robot Rapping Results Report

不妨把胜负当成点的出度和入度来看,读数据的时候维护点的度,当出现有一个点只有出度,一个点只有入度,其他的点均有出度和入度时,那两个特别的点肯定是最大的和最小的,删除顺序已知的点……如此循环直到只剩下0或1个点为止,就能认为是有序的了。

#include <bits/stdc++.h>
#include <unordered_set>

using namespace std;

const int maxn = 100010;

vector<int> win[maxn];
vector<int> lose[maxn];

int l[maxn];
int r[maxn];

int lcnt = 0;
int rcnt = 0;
int lrcnt = 0;

bool ban[maxn];

set<int> Set;

int main(){
    int n,m;
    int MAX = 0;
    int MIN = 0;

    cin>>n>>m;

    for(int i=1;i<=n;i++){
        Set.insert(i);
    }

    int N=n;

    int ans = -1;
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);

        if(ban[u] || ban[v])continue;
        win[u].push_back(v);
        lose[v].push_back(u);
        l[u]++;
        r[v]++;
        if(l[u]==1){
            lcnt++;
            if(r[u]){
                lrcnt++;
                Set.erase(u);
            }
        }
        if(r[v]==1){
            rcnt++;
            if(l[v]){
                lrcnt++;
                Set.erase(v);
            }
        }

        while(lrcnt == N-2 && lcnt==N-1 && rcnt==N-1){
            rcnt--;
            lcnt--;
            auto it = Set.begin();
            int winner = *it;
            it++;
            int loser = *it;
            Set.clear();
            if(l[loser]){
                swap(winner,loser);
            }

            ban[winner] = 1;
            ban[loser] = 1;
            for(int tt:win[winner]){
                if(ban[tt])continue;
                r[tt]--;
                if(r[tt]==0){
                    rcnt--;
                    if(l[tt]){
                        lrcnt--;
                        Set.insert(tt);
                    }
                }
            }

            for(int tt:lose[loser]){
                if(ban[tt])continue;
                l[tt]--;
                if(l[tt]==0){
                    lcnt--;
                    if(r[tt]){
                        lrcnt--;
                        Set.insert(tt);
                    }
                }
            }

            N-=2;
        }
        if(N<2){
            ans = i;
        }
    }

    cout<<ans<<endl;
    return 0;
} 

E. Intellectual Inquiry

考虑某个字符串, dp(i) 表示以字符 i 结尾的子序列的个数,那么在字符串后面添加字符j时, dp(j)=1+dp ,无论 j 是那个字符,dp(j)的结果都是一样的,所以我们总是可以选择 dp(j) 最小的那个 j 使得增益最大。又由于经过了取模,不好直接找到最小值,我们可以发现字符j如果出现得越早, dp(j) 肯定越小。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;

#define ll long long 

char str[1000010];

ll dp[26];

const int mod = 1e9+7;

int lastAppear[26];

int main(){
    memset(lastAppear,-1,sizeof(lastAppear));
    int n,k;
    cin>>n>>k;
    scanf("%s",str);
    int m = strlen(str);

    ll sum = 0;

    for(int i=0;i<m;i++){
        lastAppear[str[i]-'a'] = i;
        ll tmp = dp[str[i]-'a'];
        dp[str[i]-'a'] = sum+1;
        dp[str[i]-'a'] %= mod;
        sum = 0;
        for(int j=0;j<k;j++){
            sum+=dp[j];
        }
    }


    for(int i=0;i<n;i++){
        // find min
        int MIN = 1e9;
        int minchar=0;
        for(int j=0;j<k;j++){
            if(lastAppear[j]<MIN){
                MIN = lastAppear[j];
                minchar = j;
            }
        }

        lastAppear[minchar] = m+i;
        ll tmp = dp[minchar];
        dp[minchar] = sum+1;
        dp[minchar] %= mod;
        sum = 0;
        for(int j=0;j<k;j++){
            sum+=dp[j];
        }
    }

    ll ans = 1;
    for(int i=0;i<k;i++){
        ans+=dp[i];
        ans%=mod;
    }

    cout<<ans<<endl;

    return 0;
}
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值