看起来挺正常的一个数据结构题
结果这内存限制:8MB
题解
显然可以用bitset
而且bitset的空间开销比较小
所以我们可以用分块,对每一个块存一个bitset
块的两端就直接暴力加入bitset
最慢用时6000ms+,得分10pts
这就是我考试时的得分了。。。
考虑一下怎么优化
调整一下块的大小,发现调到n/35左右是最优的,大概用时3000ms左右
又一个非常有用的优化:
把只出现一次的数单独来计算,发现可以直接用前缀和,注意这里要把查询的区间求一下并集
把剩下的数,离散化之后可以把bitset的大小缩小一半
(这里cheat一下,通过数据,我发现可以直接把bitset大小开到28000)
最慢用时2000ms+
省出来看空间怎么用呢?
我们可以把块i~块j的答案都预处理出来,最后就可以直接把ans|=b[i][j]了
最慢用时1500ms+
又有了一个新想法
把分块暴力处理的那一部分的时间复杂度减小!
之前剔除已经了只出现一次的数,我们存下剔除之后的数组
二分一下求出每个l,r对应在简化数组中的坐标
在简化数组中进行暴力
最慢用时1300ms+
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
int L;
int a[N],tong[N],sum[N],hh[N],hcnt;
bitset<28000> b[36][36],tmp,ans;
inline int num(int x){return (x-1)/L+1;}
struct node{int l,r;}q[N];
bool cmp(node q,node w){return q.l<w.l||(q.l==w.l&&q.r<w.r);}
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
int n,m,op,i,j,k,l,r,pre=0,o,tt=0;
n=gi();m=gi();op=gi();
L=n/35;
for(i=1;i<=n;i++){a[i]=gi()-1;tong[a[i]]++;}
for(i=1;i<=n;i++){
if(tong[a[i]]>1) hh[++hcnt]=a[i];
else sum[i]++;
sum[i]+=sum[i-1];
}
sort(hh+1,hh+hcnt+1);
hcnt=unique(hh+1,hh+hcnt+1)-hh-1;
for(i=1;i<=n;i++)
if(tong[a[i]]>1)b[num(i)][num(i)].set(a[i]=(lower_bound(hh+1,hh+hcnt+1,a[i])-hh-1));
else a[i]=-1;
memset(hh,0,sizeof(hh));tt=0;
memset(tong,0,sizeof(tong));
for(i=1;i<=n;i++)
if(a[i]>=0){
hh[++tt]=a[i];
tong[tt]=i;
}
for(i=1;i<num(n);i++)
for(j=i+1;j<=num(n);j++)
b[i][j]=b[i][j-1]|b[j][j];
for(i=1;i<=m;i++){
k=gi();ans&=tmp;int tsum=0;
for(j=1;j<=k;j++){
l=gi();r=gi();
if(op&&i!=1){
l^=pre;r^=pre;
l=l%n+1;r=r%n+1;
if(l>r)swap(l,r);
}
q[j].l=l,q[j].r=r;
}
sort(q+1,q+k+1,cmp);
int _k=0,tmp=1;q[k+1].l=0x3f3f3f3f;
for(j=1;j<=k;j++){
if(q[tmp].r+1>=q[j+1].l)
q[tmp].r=max(q[tmp].r,q[j+1].r);
else{
q[++_k]=q[tmp];
tmp=j+1;
}
}
k=_k;
for(j=1;j<=k;j++){
l=q[j].l;r=q[j].r;
int _l,_r;
tsum+=sum[r]-sum[l-1];
if(num(r)-num(l)<2){
_l=lower_bound(tong+1,tong+tt+1,l)-tong;
_r=upper_bound(tong+1,tong+tt+1,r)-tong-1;
for(o=_l;o<=_r;o++)
ans.set(a[tong[o]]);
/*
for(o=l;o<=r;o++)
if(a[o]>=0)ans.set(a[o]);
*/
}
else{
_l=lower_bound(tong+1,tong+tt+1,l)-tong;
_r=upper_bound(tong+1,tong+tt+1,L*num(l))-tong-1;
for(o=_l;o<=_r;o++)
ans.set(a[tong[o]]);
/*
for(o=l;o<=L*num(l);o++)
if(a[o]>=0)ans.set(a[o]);
*/
_l=lower_bound(tong+1,tong+tt+1,L*(num(r)-1)+1)-tong;
_r=upper_bound(tong+1,tong+tt+1,r)-tong-1;
for(o=_l;o<=_r;o++)
ans.set(a[tong[o]]);
/*
for(o=L*(num(r)-1)+1;o<=r;o++)
if(a[o]>=0)ans.set(a[o]);
*/
ans|=b[num(l)+1][num(r)-1];
}
}
pre=ans.count()+tsum;
printf("%d\n",pre);
}
}
好像已经优化不了了
又想了一下,一个可以把二分去掉,进一步优化常数
但是要卡进1000ms还是非常困难啊。。。
本来整个卡常数过程不超过1.5h
结果因为电脑发生了种种问题,导致浪费了一个下午
“今天不知道为什么,电脑一直出问题,T3一开始用freopen的输入输出在本地测是0分,在lemon上是100分,后来为了卡常数,改了一下代码,在本地测没有RE,在lemon上就RE,当时以为是中病毒了。最后发现是O2的问题,有一个变量hcnt在O2的状态下被莫名其妙地赋成了一个极大值,就RE了。然后我在另一个地方开一个变量tt来代替hcnt,它就AC了。
还有,今天不知道为什么,我QQ登1min就会闪退,搜狗拼音输入法各种报错, 360杀毒杀出145个病毒,微信钉钉全部瘫痪。我现在换了妈妈的笔记本电脑,重装了GUIDE、Dev-C++、lemon才把错改完。。。估计电脑要重装系统了。。。”
最新版代码:(1200ms)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
int L;
int a[N],tong[N],sum[N],hh[N],hcnt;
bitset<28000> b[36][36],tmp,ans;
inline int num(int x){return (x-1)/L+1;}
struct node{int l,r;}q[N];
bool cmp(node q,node w){return q.l<w.l||(q.l==w.l&&q.r<w.r);}
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
int n,m,op,i,j,k,l,r,pre=0,o,tt=0;
n=gi();m=gi();op=gi();
L=n/35+1;
for(i=1;i<=n;i++){a[i]=gi()-1;tong[a[i]]++;}
for(i=1;i<=n;i++){
if(tong[a[i]]>1) hh[++hcnt]=a[i];
else sum[i]++;
sum[i]+=sum[i-1];
}
sort(hh+1,hh+hcnt+1);
hcnt=unique(hh+1,hh+hcnt+1)-hh-1;
for(i=1;i<=n;i++)
if(tong[a[i]]>1)b[num(i)][num(i)].set(a[i]=(lower_bound(hh+1,hh+hcnt+1,a[i])-hh-1));
else a[i]=-1;
memset(hh,0,sizeof(hh));tt=0;
memset(tong,0,sizeof(tong));
for(i=1;i<=n;i++)if(a[i]>=0){hh[++tt]=a[i];tong[i]=tt;}
tong[n+1]=tt+1;
for(i=n;i>=1;i--)if(!tong[i])tong[i]=tong[i+1];
for(i=1;i<num(n);i++)
for(j=i+1;j<=num(n);j++)
b[i][j]=b[i][j-1]|b[j][j];
for(i=1;i<=m;i++){
k=gi();ans&=tmp;int tsum=0;
for(j=1;j<=k;j++){
l=gi();r=gi();
if(op&&i!=1){
l^=pre;r^=pre;
l=l%n+1;r=r%n+1;
if(l>r)swap(l,r);
}
q[j].l=l,q[j].r=r;
}
sort(q+1,q+k+1,cmp);
int _k=0,tmp=1;q[k+1].l=0x3f3f3f3f;
for(j=1;j<=k;j++){
if(q[tmp].r+1>=q[j+1].l)
q[tmp].r=max(q[tmp].r,q[j+1].r);
else{q[++_k]=q[tmp];tmp=j+1;}
}
k=_k;
for(j=1;j<=k;j++){
l=q[j].l;r=q[j].r;
int _l,_r;
tsum+=sum[r]-sum[l-1];
if(num(r)-num(l)<2){
_l=tong[l];_r=tong[r+1]-1;
for(o=_l;o<=_r;o++)ans.set(hh[o]);
}
else{
_l=tong[l];_r=tong[L*num(l)+1]-1;
for(o=_l;o<=_r;o++)ans.set(hh[o]);
_l=tong[L*(num(r)-1)+1];_r=tong[r+1]-1;
for(o=_l;o<=_r;o++)ans.set(hh[o]);
ans|=b[num(l)+1][num(r)-1];
}
}
pre=ans.count()+tsum;
printf("%d\n",pre);
}
}