初学莫队找题练手,于是,我死了。 —2019.7.16晚
洛谷这都是什么神奇分类啊莫队是最慢的一个解法,常数优化是膜法!!!!!!。
上题:
树状数组与线段树的解法相似,都是离线做法,将所有询问区间记录后以右端从小到大排序,再依次解答。
题目主要解法是:在区间[l,r]中统计出现的数字的类数,我们只关心在区间内每一个数字最后一次出现的位置,如:1 3 4 2 3 1 5 7 3 。对于区间[2,5]我们只需记录4,2,和第二个3的位置,之前有多少个3都和答案无关。所以我们只需要不断更新每个数字最后出现的位置即可,新出现就该位置加1,被顶替就原位置减一,用树状数组的前缀和或线段树的区间相合性来统计答案。
树状数组:
#include
#include
#include
using namespace std;
const int maxn = 1e6+10;
int n,m,ans[maxn],inp[maxn],fst[maxn],c[maxn];
struct query{
int l,r,num;
}infor[maxn];
bool comp(query a,query b){
return a.r<b.r;
}
int lowbit(int x){
return x&(-x);
}
void add(int x,int num){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=num;
}
}
int getsum(int x){
int sum=0;
for(int i=x;i>0;i-=lowbit(i)){
sum+=c[i];
}
return sum;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&inp[i]);
}
scanf("%d",&m);
for(int i=0;i<m;i++){
scanf("%d%d",&infor[i].l,&infor[i].r);
infor[i].num=i+1;
}
sort(infor,infor+m,comp);
int st=1;
for(int i=0;i<m;i++){
for(;st<=infor[i].r;st++){
if(fst[inp[st]]){
add(fst[inp[st]],-1);
}
add(st,1);
fst[inp[st]]=st;
}
ans[infor[i].num]=getsum(infor[i].r)-getsum(infor[i].l-1);
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
#include
#include
#include
using namespace std;
const int maxn = 1e6+10;
int n,m,inp[maxn],ans[maxn],fst[maxn];
struct node{
int l,r,num;
}infor[2*maxn];
void getsum(int p){
infor[p].num=infor[p<<1].num+infor[p<<1|1].num;
}
void add(int p,int l,int r,int k){
if(l==infor[p].l&&r==infor[p].r){
infor[p].num+=k;
return;
}
int i=p<<1;
if(l<=infor[i].r){
if(r<=infor[i].r){
add(i,l,r,k);
}
else{
add(i,l,infor[i].r,k);
}
}
i++;
if(r>=infor[i].l){
if(l>=infor[i].l){
add(i,l,r,k);
}
else{
add(i,infor[i].l,r,k);
}
}
getsum(p);
}
void build_tree(int p,int l,int r){
infor[p].l=l;
infor[p].r=r;
if(l==r){
return;
}
int mid=(l+r)>>1;
build_tree(p<<1,l,mid);
build_tree(p<<1|1,mid+1,r);
}
int getans(int p,int l,int r){
if(l==infor[p].l&&r==infor[p].r){
return infor[p].num;
}
if(l<=infor[p<<1].r&&r<=infor[p<<1].r){
return getans(p<<1,l,r);
}
else if(l<=infor[p<<1].r&&r>=infor[p<<1|1].l){
return getans(p<<1,l,infor[p<<1].r)+getans(p<<1|1,infor[p<<1|1].l,r);
}
else{
return getans(p<<1|1,l,r);
}
}
struct query{
int l,r,num;
}ask[maxn];
bool comp(query a,query b){
return a.r<b.r;
}
int main(){
scanf("%d",&n);
build_tree(1,1,n);
for(int i=1;i<=n;i++){
scanf("%d",&inp[i]);
}
scanf("%d",&m);
for(int i=0;i<m;i++){
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].num=i;
}
sort(ask,ask+m,comp);
int st=1;
for(int i=0;i<m;i++){
for(;st<=ask[i].r;st++){
if(fst[inp[st]]){
add(1,fst[inp[st]],fst[inp[st]],-1);
}
fst[inp[st]]=st;
add(1,st,st,1);
}
ans[ask[i].num]=getans(1,ask[i].l,ask[i].r);
}
for(int i=0;i<m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
主席树我写的是在线的解法,因为主席树保存了所有状态下的数据,所以直接O(logn)查询就行了。在主席树解法中,我们要保存的是每个数值它的下一个出现的位置,并给此处加1。查询时查有多少个大于区间右端(下一次出现在区间外,即原值是此区间内最后一次出现)的值就行了。
主席树:
#include
#include
using namespace std;
const int maxn = 1e6+10;
const int mx = 5e5+10;
int n,m,inp[mx],nx[maxn],last[maxn],root[mx],cnt,sum;
struct node{
int lson,rson,num;
}infor[30*mx];
void update(int &p,int old,int l,int r,int tag){
p=++cnt;
infor[p]=infor[old];
infor[p].num++;
if(l==r){
return;
}
int mid=(l+r)>>1;
if(tag<=mid){
update(infor[p].lson,infor[p].lson,l,mid,tag);
}
else{
update(infor[p].rson,infor[p].rson,mid+1,r,tag);
}
}
int query(int l,int r,int ql,int qr,int tag){
if(l==r){
return infor[qr].num-infor[ql].num;
}
int mid=(l+r)>>1;
if(tag<=mid){
return query(l,mid,infor[ql].lson,infor[qr].lson,tag)+infor[infor[qr].rson].num-infor[infor[ql].rson].num;
}
else{
return query(mid+1,r,infor[ql].rson,infor[qr].rson,tag);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&inp[i]);
if(last[inp[i]]){
nx[last[inp[i]]]=i;
}
last[inp[i]]=i;
}
for(int i=1;i<=n;i++){
if(!nx[i]){
nx[i]=n+1;
}
}
for(int i=1;i<=n;i++){
update(root[i],root[i-1],1,n+1,nx[i]);
}
scanf("%d",&m);
for(int i=0;i<m;i++){
sum=0;
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(1,n+1,root[l-1],root[r],r+1));
}
return 0;
}
最后莫队解法。莫队就是一个优化O(n n \sqrt{n} n)的大暴力,套板子就行了。不过最后两个点要奇偶排序加上常数优化再加吸氧才能过。(注释掉的是80分的解法)
#include
#include
#include
#include
using namespace std;
const int maxn = 1e6+10;
int ans[maxn],inp[maxn],n,m,block,tmp,cnt[maxn];
struct query{
int l,r,num,bl;
}infor[maxn];
//void Add(int pos){
// if(!cnt[inp[pos]]){
// tmp++;
// }
// cnt[inp[pos]]++;
//}
//void del(int pos){
// if(cnt[inp[pos]]==1){
// tmp--;
// }
// cnt[inp[pos]]--;
//}
//bool comp(query a,query b){
// return a.bl==b.bl?a.r
//}
bool comp(query a, query b) {
return (a.bl^b.bl)?a.bl<b.bl:(a.bl&1)?a.r<b.r:a.r>b.r;
}
int main(){
scanf("%d",&n);
block = sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&inp[i]);
}
scanf("%d",&m);
for(int i=0;i<m;i++){
scanf("%d%d",&infor[i].l,&infor[i].r);
infor[i].num=i+1;
infor[i].bl=infor[i].l/block;
}
sort(infor,infor+m,comp);
int l=1,r=0;
for(int i=0;i<m;i++){
int ql=infor[i].l,qr=infor[i].r;
// while(l
// del(l++);
// }
// while(l>ql){
// Add(--l);
// }
// while(r
// Add(++r);
// }
// while(r>qr){
// del(r--);
// }
while(l < ql) tmp -= !--cnt[inp[l++]];
while(l > ql) tmp += !cnt[inp[--l]]++;
while(r < qr) tmp += !cnt[inp[++r]]++;
while(r > qr) tmp -= !--cnt[inp[r--]];
ans[infor[i].num]=tmp;
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}