• 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 }
  • 相关阅读:
    如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?
    FileUpload拦截器
    aspnet网页刷新
    查看SQL表的详细信息
    学习GDI+ (1)
    设计模式简单工厂模式
    对数据库表操作,统一的方法。
    随机产生300道四则运算
    略谈从计算机专业,再到软件构建的初识
    #在android studio中维护日程管理系统
  • 原文地址:https://www.cnblogs.com/fu3638/p/7209045.html
Copyright © 2020-2023  润新知