Educational Codeforces Round 96 (Rated for Div. 2)

A. Number of Apartments

  因为数据范围很小,可以随便搞搞。
  1、从3,5,7开始转移合法
  2、暴力n^2
  3、稍微打下表

B. Barrels

  简单贪心

C. Numbers on Whiteboard

  观察一下,最后剩下肯定为2
  猜测肯定不是唯一输出,那么我每次取最后两个处理,再放回去

D. String Deletion

  要求步数最多,即每次删去最少的
  考虑到删去中间会影响后续步骤,因此需要具体观察
  1      花费步骤x1  
  11111    花费步骤x1
  110     花费步骤x2  先删去首个1,再删去跟着的1
  01111    花费步骤x2  先删去末尾的1,再删去起始的0
 
  10      花费步骤x1
  101     花费步骤x2  先删去末位1,再删去首位1
  1010     花费步骤x2  先删去末位0,再删去首位1  删去末位1,再删去首位0
  101010    花费步骤x3
  观察以上情况:
  1、长度为1的串,花费一步删去
  2、连续相同的串,花费一步删去
  3、首位长度为1,先处理末位
  4、01交错的情况,一次删去末位再删去首位
  5、首位长度大于2,花费一步删去首位
  发现需要考虑连续相同的情况,将连续且相同的元素视为一个段。
  发现删去中间位置时,会导致连续的情况出现,可能会影响后续步骤
 
  总结规律:
  如果只有一个段,那么删去这个段。
  如果首段长度大于等于2,那么花费一步删去首段。
  如果首段长度等于1
    如果后面有长度大于等于2的段,就从后面的该段删去一位,再删去首段。
    否则,出现01交错,一次删去末段再删去首段
#include<cstdio>
#include<cmath>
#include<iostream> 
#include<cstring> 
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<iomanip>
#define inf 24300000000000001
#define ll long long
using namespace std;
int t,cnt,l,ans,font,pos,mx,n;
char s[300007];
struct {
    int l,r,len;
}node[300007];
int main(){
    scanf("%d",&t);
    while(t--){
        cnt=0;l=1;ans=0;
        scanf("%d",&n);
        scanf("%s",s+1);
        for(int i=1;i<=n;++i){
            if(s[i+1]!=s[i]){
                node[++cnt].r=i;
                node[cnt].l=l;
                node[cnt].len=i-l+1;
                l=i+1;
            }
        }
        font=1;pos=1;mx=cnt;
        while(cnt>0){
            if(node[font].len!=1){
                ans++;
                font++;
                cnt--;
            }
            else{
                int flag=0;
                pos=max(pos,font);
                for(;pos<=mx;++pos){
                    if(node[pos].len!=1){
                        flag=1;
                        node[pos].len--;
                        font++;
                        ans++;
                        cnt--;
                        break;
                    }
                }
                if(!flag){
                    ans+=cnt/2;
                    if(cnt%2) ans++;
                    break;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

E. String Reversal

  举个例子

  1 2 3 4 5 

  c a c b a

  倒置之后

  a b c a c

  2 4 1 5 3

  按目标串顺序来,首先我们要把原串中a换到前面去,想一想,编号2的a更合适。因为只要交换1次。

  然后呢,把b换到前面去,交换两次就了,因为编号2的a已经到前面去了,要交换2次。

  在之后呢,要把c交换到前面去,现在发现又有两个c给我们换了,选哪个呢?

  想一想,当然是编号1的c交换到第三个位子,编号3的交换到第五个位子了。

  要交换吗?我们发现不用了,因为原串里编号1的c前面没有需要交换的字母了。

  然后再交换a到第四个位子,让编号5的a来吧。它前面有四个字母。

  现在目标串已经有了三个字母,让它去发现只要交换1次。

  那么我们发现交换四次就行。

  那么代码部分,按照我们的顺序来模拟,怎么思考前面有几个要交换的字母呢。

  首先我们在许多个候选里面要选一个,当然是最前面的。

  然后呢,他得要考虑前面还要几个交换的字母,已经交换到目标位置的我们就不考虑进去了,因为交换不到那个位子了。

  打个标记vis?我们用树状数组记录每个位置有无交换到目标位置更直接更方便对不对。

  
#include<cstdio>
#include<cmath>
#include<iostream> 
#include<cstring> 
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<iomanip>
#define inf 24300000000000001
#define ll long long
using namespace std;
int n;
ll ans;
char s[200007];
int c[200007];
queue<int>q[50];
inline int lowbit(int x){
    return x&-x; 
}
int sum(int x){
    ll res=0;
    while(x>0){
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v){
    while(x<=n){
        c[x]+=v;x+=lowbit(x);
    }
} 
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;++i){
        q[s[i]-'a'+1].push(i);
        add(i,1);
    }
    ans=0;
    for(int i=n;i>=1;--i){
        int p=q[s[i]-'a'+1].front();q[s[i]-'a'+1].pop();
        ans+=sum(p)-1;
        add(p,-1);
    }
    printf("%lld\n",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/PdrEam/p/13806218.html