分块-bzoj2821: 作诗(Poetize)
2017-02-22 18:28
253 查看
http://hzwer.com/3663.html
我的程序好垃圾,常数好像很大的样子,本来是WA,然后改好之后交上去超时;
改来改去半天还是TLE,后来把分块长度变成sqrt(n/log2(n)),就过了;
k=sqrt(n/log(n))/log(2)
然而我自己并不会求这个;
唉数学差;
黄学长讲的很详细了;
因为是强制在线,我们先尽可能多的先预处理;
就是那个f[i][j];
因为我们的预处理,所以对于一个区间,我们需要暴力求解的数字最多不过两个块,区间里成块的都算好了放在f数组里面了;
对于不在整块里的数字,我们按黄学长说的就好啦;
但是统计次数的时候,我们用前坠和就炸;
所以我们要用前向星,排序一遍,把相同的数字按原始下标有序地排到一起;这样我们就可以通过两次二分来求出某数字在某区间出现的次数了;
代码思路是很清晰的,应该也好理解,就是跑得有点慢…..
我的程序好垃圾,常数好像很大的样子,本来是WA,然后改好之后交上去超时;
改来改去半天还是TLE,后来把分块长度变成sqrt(n/log2(n)),就过了;
k=sqrt(n/log(n))/log(2)
然而我自己并不会求这个;
唉数学差;
黄学长讲的很详细了;
因为是强制在线,我们先尽可能多的先预处理;
就是那个f[i][j];
因为我们的预处理,所以对于一个区间,我们需要暴力求解的数字最多不过两个块,区间里成块的都算好了放在f数组里面了;
对于不在整块里的数字,我们按黄学长说的就好啦;
但是统计次数的时候,我们用前坠和就炸;
所以我们要用前向星,排序一遍,把相同的数字按原始下标有序地排到一起;这样我们就可以通过两次二分来求出某数字在某区间出现的次数了;
代码思路是很清晰的,应该也好理解,就是跑得有点慢…..
#include<cstdio>//cfb #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> using namespace std; struct kuai{//就是分块 int ll,rr;//这个是区间范围 }c[1000]; struct cs{//a数组有两个用途,x,num是用来排序的;ll,rr,代表排序后数字i出现的范围 int x,num,ll,rr; }a[100001]; int f[1000][1000],b[100001],aa[100001];//b是一个计数器,aa就是存放读入的数组; int n,m,ll,x,y,ans,z,q; inline int read()//黄学长的 { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } bool cmp(cs x,cs y){if(x.x!=y.x)return x.x<y.x;return x.num<y.num;} void make(){ int l=1,k=sqrt(n/log(n))/log(2); while(l<=n){//分块; c[++ll].ll=l; c[ll].rr=min(l+k-1,n); l+=k; } for(int i=1;i<=ll;i++){ int sum=0; for(int j=i;j<=ll;j++){ for(int k=c[j].ll;k<=c[j].rr;k++){ b[a[k].x]++; if(b[a[k].x]>1&&(b[a[k].x]&1))sum--; if(!(b[a[k].x]&1))sum++; } f[i][j]=sum; } for(int j=c[i].ll;j<=c[ll].rr;j++)b[a[j].x]=0;//比memset快好多; } sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++){if(!a[a[i].x].ll)a[a[i].x].ll=i;a[a[i].x].rr=i;} } int max_min(int x,int y,int z){//在x,y区间里找到比z大的最小的数,用于区间左端; int ans=y; while(y>=x){ int mid=(y+x)>>1; if(a[mid].num>=z){ ans=min(ans,mid); y=mid-1; }else x=mid+1; } return ans; } int min_max(int x,int y,int z){//在x,y区间里找到比z小的最大的数,用于区间左端; int ans=x; while(y>=x){ int mid=(y+x)>>1; if(a[mid].num<=z){ ans=max(ans,mid); x=mid+1; }else y=mid-1; } return ans; } void check(int num,int x,int y,int l,int r){ x=max_min(a[num].ll,a[num].rr,x); y=min_max(a[num].ll,a[num].rr,y); l=max_min(a[num].ll,a[num].rr,l); r=min_max(a[num].ll,a[num].rr,r); x=y-x+1; y=r-l+1;//通过二分出来的下标来统计区间内数字出现次数 if(!y&&x&&!(x&1))ans++;//我一开始漏掉了Y=0的情况 if(y&&(y&1)&&!(x&1))ans++; if(y&&!(y&1)&&(x&1))ans--; } void outit(int x,int y){ int first=0,last=0; for(int i=1;i<=ll;i++)if(x<=c[i].ll&&c[i].rr<=y){if(!first)first=i;last=i;} ans=f[first][last];//一大块的答案树算好了的 if(last){ for(int i=x;i<=c[first].ll-1;i++)if(!b[aa[i]]){check(aa[i],x,y,c[first].ll,c[last].rr),b[aa[i]]=1;} for(int i=y;i>=c[last ].rr+1;i--)if(!b[aa[i]]){check(aa[i],x,y,c[first].ll,c[last].rr),b[aa[i]]=1;} for(int i=x;i<=c[first].ll-1;i++)b[aa[i]]=0; for(int i=y;i>=c[last ].rr+1;i--)b[aa[i]]=0; }else{//找不到完整块的处理 for(int i=x;i<=y;i++) if(!b[aa[i]]){ b[aa[i]]=1; int l=max_min(a[aa[i]].ll,a[aa[i]].rr,x); int r=min_max(a[aa[i]].ll,a[aa[i]].rr,y); if(!((r-l+1)&1))ans++; } for(int i=x;i<=y;i++)b[aa[i]]=0; } printf("%d\n",ans); } int main() { n=read();q=read();m=read(); for(int i=1;i<=n;i++)a[i].x=read(),a[i].num=i,aa[i]=a[i].x; make(); while(m--){ x=read(),y=read(); outit(min((x+ans)%n+1,(y+ans)%n+1),max((x+ans)%n+1,(y+ans)%n+1));//强制在线 } }
相关文章推荐
- BZOJ 2821 作诗(Poetize)(分块)
- [bzoj] 2821 作诗(Poetize) || 分块
- bzoj 2821: 作诗(Poetize) (分块)
- BZOJ 2821 作诗(Poetize) 分块
- [BZOJ 2821] 作诗(Poetize) 【分块】
- bzoj2821: 作诗(Poetize)【分块】
- 【分块】bzoj2821 作诗(Poetize)
- 【bzoj2821】作诗(Poetize) 分块
- BZOJ 2821 作诗(Poetize) 分块
- BZOJ 2821: 作诗(Poetize) [分块]
- BZOJ 2821 作诗(Poetize) 分块
- BZOJ 2821 作诗(Poetize) 分块
- 【BZOJ2821】作诗(Poetize) 分块
- BZOJ 2821: 作诗(Poetize)|分块
- BZOJ2821 作诗(Poetize) 【分块】
- bzoj 2821: 作诗(Poetize) 分块
- bzoj 2821: 作诗(Poetize)【分块】
- 【分块】BZOJ2821 作诗(Poetize)
- BZOJ 2821: 作诗(Poetize)( 分块 )
- 【bzoj2821】作诗(Poetize)