Educational Codeforces Round 150 (Rated for Div. 2)

今晚看完电影再回来补(到宿舍11点半了所以会写的简便点)

A.首先手玩一下

2的时候稳后手赢

3:1 2 和3都是后手赢

4:变成1 1 2或者1 3都是后手赢

5:直接1 4| | 1 1 3| |1 1 1 2 找到了 1 1 3先手能赢

好了后面不用找了,先手直接全部变成 1 1 x就行

所以n<=4bob赢,否则 alice赢

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;
typedef pair PII;
int n,m,k;
PII a[N];
int f[N];
void solve()
{
    cin>>n;
    if(n<=4) cout<<"Bob\n";
    else cout<<"Alice\n";
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
}

B.手玩一下添加的数要一直非递减,如果遇到一个转折点,要判断是不是小于开头(开头是前面最小的),然后后面就要继续在这个转折点后递增且小于等于开头的数

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;
int n,m,k;
void solve(){
    cin>>n;
    bool f=false;
    vector a;
    for(int i=1;i<=n;i++){
        int x;cin>>x;
        if(a.empty()||(f==false&&x>=a.back())){
            cout<<"1";
            a.push_back(x);
        }
        else if(f==false&&x=a.back()&&x<=a[0]){
                cout<<1;a.push_back(x);
            }
            else cout<<0;
        }
    }
    cout<<"\n";
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
}

C.常规套路是枚举换成哪个字符的

然后就要想一些性质了

比如dcbbbcaddddc

把c变小,第一个c影响了前面小于他的点,但是后面还有c所以等于没改变结果,中间的点c影响了前面比它小的点,但后面又有个c所以不改变前面的点也等于没改变结果,最后一个点c变小,前面小于c的点是全部改变的,所以改最后一个点才有影响

把c变大,其实这里可以直观的想,变大了希望前面受到影响最小,让前面尽可能的正数,最好是放在最左边的c

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;

int n,m,k;
int w[N];
int ch[N][26];
void init(){
    w[0]=1;
    for(int i=1;i<=5;i++)
    w[i]=w[i-1]*10;
}
void solve()
{
    string s;
    cin>>s;
    n=s.size();
    s="?"+s;
    vector> pos(5);
    for(int i=1;i<=n;i++)
    pos[s[i]-'A'].push_back(i);

    function get=[&](string s)->long long{
        int mx=0;
        long long cnt=0;
        for(int i=n;i>=1;i--){
            if(s[i]-'A'>t;
    while(t--) solve();
}

D.分成k/2对,且要每对有交集,和其他没交集,等于两个交集的在一起,且其他和这两个没交集

直接dp即可

首先按照右端点排序(左端点会wa),因为如果按照左端点排序,如果这个区间很长,但是左端点很小,只能和前面的端点结合,但是它其实还能和被自己包含的区间结合的

然后dp即可

状态:前完了前i-1个区间能组成k/2对,且以i为最后一对的第二个区间的最大值

直接dp,说明这里可以预处理或者直接二分降低

第一个朴素版方便读者看懂状态转移

第二个优化的

这两个都能ac

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;
typedef pair PII;
int n,m,k;
PII a[N];
int f[N];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second,f[i]=0;
    //vector f(n+1,0);
    sort(a+1,a+1+n,[&](const PII p,const PII q){
        return p.second>t;
    while(t--) solve();
}
#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;
typedef pair PII;
int n,m,k;
PII a[N];
int f[N];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second,f[i]=0;
    //vector f(n+1,0);
    sort(a+1,a+1+n,[&](const PII p,const PII q){
        return p.second>1;
                    if(a[mid].second>t;
    while(t--) solve();
}

E.

首先可以贪心的让连续的空格尽可能长

OOOXOOOO

OOXOOOOO

如果能放5个

可以直接放连续5个O第二那里,贡献是4,如果放在第一行右边4个左边一个贡献3

右边3个左边两个贡献3

可以手完一下,连续的空格尽可能长是最优的

可以直接统计有多少个长度为x的连续O字串

然后直接从长度大的遍历到小的就行l

然后就有下面的代码

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;
typedef pair PII;
#define int long long
int n,m,k;
int a[N];
void solve()
{
    cin>>n;
    vector> g(n+1,vector(n+1,0));
    for(int i=1;i<=n;i++){
        int x;cin>>x;
        for(int j=1;j<=x;j++)
            g[j][i]=1;
    }
    vector cnt(n+1,0);
    cin>>m;
    for(int i=1;i<=n;i++)
    {
        int now=0;
        for(int j=1;j<=n;j++){
            if(g[i][j]==1){
                cnt[now]++;now=0;
            }
            else now++;
        }
        cnt[now]++;
    }
    int res=0;
    for(int i=n;i>=2;i--)
    {
        int t=min(cnt[i],m/i);
        res+=t*(i-1);
        cnt[i-1]+=cnt[i]-t;
        m-=t*i;
    }
    cout<>t;
    while(t--) solve();
}

然后就tle或者mle了

优化

从n开始倒叙枚举,用set记录哪一行后面行已经有有X了,因为X是直接前x行都有X的,只要记录后面最早有就行了,这样前面行肯定就有

举个例子把

XOOXOX

XOOXOX

XOOOOX

XOOOOO

后面的行已经存在的,前面行也一定存在

这里设置左右边界就简单很多

#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int N = 2e5+10;
#define int long long
typedef pair PII;
int n,m,k;
int a[N];
void solve()
{
    cin>>n;
    vector cnt(n+1);
    vector tim(n+2);
    vector> pos(n+1);
    for(int i=1;i<=n;i++){
        int x;cin>>x;
        pos[x].push_back(i);
    }
    cin>>m;
    set s;
    s.insert(0);s.insert(n+1);
    tim[0]=tim[n+1]=n;
    for(int i=n;i>=0;i--){
        for(auto&x:pos[i]){
            auto it=s.lower_bound(x);
            int pre=*prev(it);
            int nxt=*it;
            int from=min(tim[pre],tim[nxt]);
            cnt[nxt-pre-1]+=from-i;
            s.insert(x);
            tim[x]=i;
        }
    }
    int ans=0;
    for(int i=n;i>=1;i--)
    {
        int t=min(cnt[i],m/i);
        ans+=t*(i-1);
        cnt[i]-=t;
        cnt[i-1]+=cnt[i];
        m-=t*i;
    }
    cout<>t;
    while(t--) solve();
}

你可能感兴趣的:(算法)