[CTSC2017]密钥

一个环 有 2n+1 2 ∗ n + 1 个位置, 选择 n n 个位置填上//n A A n B B
….题目太长了 自己查吧qwq

有一点很容易想到就是把 A B B 一个看成 1 一个看成 1 − 1 空位看成 B B
然后枚举空位的位置为x 快速计算答案。
我们发现
对于 i>x : sum[i]>sum[x] s u m [ i ] > s u m [ x ] i i 位置为A 即可对答案有贡献
对于 i<x i < x : sum[n]sum[x]+sum[i]>0 s u m [ n ] − s u m [ x ] + s u m [ i ] > 0 即可对答案有贡献
整理得: sum[i]>sum[x]+1 s u m [ i ] > s u m [ x ] + 1
可以数据结构维护这个拿到部分分。 O(nlogn) O ( n l o g n )
然后我们发现 每个相邻的 sum[x] s u m [ x ] 只差 1 1 。我们可以暴力记录下所有A sum s u m 然后每次 O(1) O ( 1 ) 计算答案。
整体复杂度 O(n) O ( n ) 注意思考一下修改枚举过的 sum s u m 数组值时应该新开数组记录。 即 bucc b u c c

#include <bits/stdc++.h>
using namespace std;

const int base=10000000;

int sum[20000010],p[20000010],buc[20000010],bucc[20000010];
int seed,n,k,S;  

int getrand(){  
    seed=((seed*12321)^9999)%32768;  
    return seed;  
}  
void generateData(){  
    scanf("%d%d%d",&k,&seed,&S);  
    int t=0;  
    n=k*2+1;  
    memset(p,0,sizeof(p));  
    for(int i=1;i<=n;i++)  
    {  
        p[i]=(getrand()/128)%2;  
        t+=p[i];  
    }  
    int i=1;  
    while(t>k)  
    {  
        while(p[i]==0)  
            i++;  
        p[i]=0;  
        t--;  
    }  
    while(t<k){  
        while(p[i]==1)  
            i++;  
        p[i]=1;  
        t++;  
    } 
}

int ans=0,pos,pos2,pos3;

//sum[i]>sum[x]
//sum[n]-sum[x]+sum[i]>0
//sum[i]>sum[x]+1

//sum[] 

int main(){
    generateData();
    for(int i=1;i<=n;i++)sum[i]=p[i]?sum[i-1]+1:sum[i-1]-1;
    for(int i=1;i<=n;i++){
        if(p[i]){
            buc[sum[i]+base]++;
        }
    }
    for(int i=2;i<=n;i++)
        if(sum[i]>sum[1]&&p[i])ans++;
    if(!p[1]&&ans==0)pos=1;
    if(!p[1]&&ans==S)pos2=1;
    if(p[1])buc[sum[1]+base]--,buc[sum[1]+base-1]++;
    int now=sum[1]+base+1;
//  puts("qwq");
    for(int x=2;x<=n;x++){
    //  cout<<ans<<endl;
        if(p[x]){
            ans-=buc[now++];
            buc[sum[x]+base]--,buc[sum[x]+base-1]++;
        }
        else {
            ans+=buc[--now];
            if(ans==0)pos=x;
            else if(ans==S)pos2=x;
        }
    }
//  cout<<ans<<endl;
    //puts("qaq");
    //    sum[i]>sum[x]
    //    sum[n]-sum[x]+sum[i]>0
    //    sum[i]>sum[x]-1
    ans=0;
    memset(buc,0,sizeof(buc));
    for(int i=1;i<=n;i++)sum[i]=p[i]?sum[i-1]-1:sum[i-1]+1;
    for(int i=1;i<=n;i++){
        if(!p[i]){
            buc[sum[i]+base]++;
        }
    }
    for(int i=2;i<=n;i++)
        if(sum[i]>sum[1]&&(!p[i]))ans++;
    if(!p[1]&&ans==S)pos3=1;
//  for(int i=1;i<=20000005;i++)bucc[i]=buc[i];
//  if(!p[1])bucc[sum[1]+base]--,bucc[sum[1]+base+1]++;
    now=sum[1]+base+1;
//  cout<<ans; 1 2 3 4 3 2 1 0 -1 0 1

    for(int x=2;x<=n;x++){
    //  cout<<ans<<endl;
        if(!p[x]){
            ans-=buc[now++];
            if(!p[x-1])buc[sum[x-1]+base]--,buc[sum[x-1]+base+1]++;
        //  bucc[sum[x]+base]--,bucc[sum[x]+base+1]++;
            if(ans==S)pos3=x;
        }
        else {
            ans+=buc[--now];
            if(!p[x-1])buc[sum[x-1]+base]--,buc[sum[x-1]+base+1]++;
        }
    }
    cout<<pos<<endl<<pos2<<endl<<pos3<<endl;
    return 0;
 }
 //0 0 0 0 1 1 1 1 1 0 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值