zoukankan      html  css  js  c++  java
  • Codeforces 351D Jeff and Removing Periods(莫队+区间等差数列更新)

    题目链接:http://codeforces.com/problemset/problem/351/D

    题目大意:有n个数,每次可以删除掉数值相同并且所在位置成等差数列的数(只删2个数或者只删1个数应该也是可以的),删掉这些数以后可以将剩下的数重新以任意顺序排列,称为一次操作。现在给出m个询问,每个询问一个区间[l,r],问删光区间[l,r]中的数最少需要的操作次数。

    解题思路: 由于一次操作之后可以以任意顺序排序,所以可以把相同数字排成等差的以便删除,那接下来就只要判断这个区间还有几种数字,一种数字操作一次就可以了,这个用莫队就能很快解决。但是问题在于第一次操作能不能将其中一种数字删尽,也就是在未排序情况下该区间是否有一种数字是成等差数列的。可以通过设置两个数组fl[i],fr[i],让fl[i]记录往左a[i]第一次不成等差的位置,fr[i]记录往右a[i]第一次不成等差的位置。这样只要相应地对add(),remove()做出修改就可以更新一段区间内的等差数列的个数了。具体看代码。

    还有我个人犯的几个小错误,注意一下:

    ①使用的l,r应当是当前的L,R而不是查询区间的l,r。

    ②一开始没把add,remove弄明白,remove(pos)里的pos是即将要去掉的那个位置,add(pos)里的pos是即将要加上的那个位置

    ③fl[i]应该处理成a[i]往左找第一个不成等差的位置,而不是最后一个成等差的位置(fr[i]同理),比如1 2 1 3 5这组对1操作就会出错
    (把i<=n写出i<=m,题目前面十几个数据都是n=m,害我一下没看出来,我脑抽了竟然找了半天)

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<math.h>
      4 #include<stdio.h>
      5 #include<cstring>
      6 using namespace std; 
      7 const int N=1e5+5;
      8 
      9 int ans,unit,dc;
     10 //fl[i]记录a[i]往左第一个不成等差的位置,fr[i]记录a[i]往右第一个不成等差的位置
     11 //pre[i]记录从左往右前一个a[i]的位置,bak[i]记录从右往左前一个a[i]的位置,last[a[i]]记录a[i]最后出现位置 
     12 int a[N],cnt[N],res[N],pre[N],bak[N],last[N],fl[N],fr[N];
     13 
     14 struct node{
     15     int l,r;
     16     int id;
     17 }q[N];
     18 
     19 bool cmp(node a,node b){
     20     return a.l/unit==b.l/unit?a.r<b.r:a.l/unit<b.l/unit;    
     21 }
     22 
     23 void addl(int pos,int r){
     24     cnt[a[pos]]++;
     25     if(cnt[a[pos]]==1){
     26         dc++;
     27         ans++;
     28     }
     29     
     30     else if(fr[pos]<=r&&fr[bak[pos]]>r) dc--;//加上a[pos]之后不成等差&&加上之前成等差
     31 }
     32 
     33 void removel(int pos,int r){
     34     cnt[a[pos]]--;
     35     if(cnt[a[pos]]==0){
     36         dc--;
     37         ans--;
     38     }
     39     else if(fr[pos]<=r&&fr[bak[pos]]>r)    dc++;//去掉a[pos]之前不成等差&&去掉之后成等差
     40 }
     41 
     42 void addr(int pos,int l){
     43     cnt[a[pos]]++;
     44     if(cnt[a[pos]]==1){
     45         dc++;
     46         ans++;
     47     }
     48     else if(fl[pos]>=l&&fl[pre[pos]]<l)    dc--;//同理 
     49 }
     50 
     51 void remover(int pos,int l){
     52     cnt[a[pos]]--;
     53     if(cnt[a[pos]]==0){
     54         dc--;
     55         ans--;
     56     }
     57     else if(fl[pos]>=l&&fl[pre[pos]]<l)    dc++;
     58 }
     59 
     60 int main(){
     61     int n;
     62     scanf("%d
    ",&n);
     63     unit=sqrt(n);
     64     for(int i=1;i<=n;i++){
     65         scanf("%d",&a[i]);
     66     }
     67     //预处理fl,fr 
     68     for(int i=1;i<=n;i++){
     69         pre[i]=last[a[i]];
     70         last[a[i]]=i;
     71         //从左往右,当a[i]个数小于等于两个时,左边界为0 
     72         if(pre[pre[i]]==0)
     73             fl[i]=0;
     74         else if(pre[pre[i]]-pre[i]==pre[i]-i)
     75             fl[i]=fl[pre[i]];
     76         else
     77             fl[i]=pre[pre[i]];
     78     }
     79     //清空last 
     80     memset(last,0,sizeof(last));
     81     for(int i=n;i>=1;i--){
     82         bak[i]=last[a[i]];
     83         last[a[i]]=i;
     84         //从右往左,当a[i]个数小于等于两个时,右边界为n+1 
     85         if(bak[bak[i]]==0)
     86              fr[i]=n+1;
     87         else if(bak[bak[i]]-bak[i]==bak[i]-i)
     88             fr[i]=fr[bak[i]];
     89         else
     90             fr[i]=bak[bak[i]];
     91     }
     92     
     93     
     94     int m;
     95     scanf("%d",&m);
     96     for(int i=1;i<=m;i++){
     97         scanf("%d%d",&q[i].l,&q[i].r);
     98         q[i].id=i;
     99     }
    100     
    101     sort(q+1,q+1+m,cmp);
    102     
    103     int L=q[1].l,R=L-1;
    104     for(int i=1;i<=m;i++){
    105         //注意传入add,remove的左右端点是L,R而不是q[i].l,q[i].r 
    106         while(L>q[i].l)
    107             addl(--L,R);
    108         while(L<q[i].l)
    109             removel(L++,R);
    110         while(R<q[i].r)
    111             addr(++R,L);
    112         while(R>q[i].r)
    113             remover(R--,L);
    114         if(dc>0)
    115             res[q[i].id]=ans;
    116         else
    117             res[q[i].id]=ans+1;
    118     }
    119     
    120     for(int i=1;i<=m;i++){
    121         printf("%d
    ",res[i]);
    122     }
    123 }
  • 相关阅读:
    FZU 2150 Fire Game
    POJ 3414 Pots
    POJ 3087 Shuffle'm Up
    POJ 3126 Prime Path
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    字符数组
    HDU 1238 Substing
    欧几里德和扩展欧几里德详解 以及例题CodeForces 7C
    Codeforces 591B Rebranding
  • 原文地址:https://www.cnblogs.com/fu3638/p/7209045.html
Copyright © 2011-2022 走看看