已补(3/9)
A
B
C
D
E
题意
n n n个人,在线询问区间内选择尽可能多的人,每个人有类别,保证每个类别选择的人数不超过 k k k
题解
事实上,做过类似的题。
我们之间做过一道题是维护上一个出现的位置,这次就是维护前
k
k
k个出现的位置,只要这个位置是小于
l
l
l的。那么当前的数就可以被加入。
也就是对于区间内,可以使用权值线段树,那么显然就是主席树了。
很裸的主席树,思想也很套路,但是没想出来。属实脑瘫。
#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define RFOR(i,a,b) for(int i=a;i>=b;i--)
#define sf(x) scanf("%d",&x)
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 100000+500;
const ll mod = 1e9+7;
int tot,n,m,k;
int sum[(maxn<<5)+10],rt[maxn+10],ls[(maxn<<5)+10],rs[(maxn<<5)+10];
int a[maxn+10],b[maxn+10],len=3e5+10;
vector<int>G[maxn];
int build(int l,int r){
int root=++tot;
if(l==r)return root;
int mid=(l+r)>>1;
ls[root]=build(l,mid);
rs[root]=build(mid+1,r);
return root;
}
int update(int k,int l,int r,int root){
int dir=++tot;
ls[dir]=ls[root],rs[dir]=rs[root],sum[dir]=sum[root]+1;
if(l==r)return dir;
int mid=(l+r)>>1;
if(k<=mid)ls[dir]=update(k,l,mid,ls[dir]);
else rs[dir]=update(k,mid+1,r,rs[dir]);
return dir;
}
int query(int k,int l,int r,int u,int v){
int mid=(l+r)>>1;
if(l==r)return sum[v]-sum[u];
if(k<=mid)return query(k,l,mid,ls[u],ls[v]);
else return sum[ls[v]]-sum[ls[u]]+query(k,mid+1,r,rs[u],rs[v]);
}
int main(){
cin>>n>>k;
FOR(i,1,n)sf(a[i]);
FOR(i,1,n){
int len=G[a[i]].size();
if(len>=k)b[i]=G[a[i]][len-k]+1;
else b[i]=1;
G[a[i]].push_back(i);
}
rt[0]=build(1,n);
FOR(i,1,n)rt[i]=update(b[i],1,n,rt[i-1]);
int l=0,r=0,last=0;
cin>>m;
FOR(i,1,m){
sf(l),sf(r);
l=(l+last)%n+1;
r=(r+last)%n+1;
if(l>r)swap(l,r);
last=query(l,1,n,rt[l-1],rt[r]);
printf("%d\n",last);
}
}
F
2000 G
题意
求所有子段的最大值-最小值的和
题解
对于每个子段,都有某个点作为最大值和某个点作为最小值,反过来对于第
i
i
i个点,它作为最大值或者最小值会影响哪些区间。通过这点进行计算。
怎么得到他能影响的最远区域呢?
单调栈,如果维护最大值,每次加入新的都会把之前小于它的弹出,只留下比它大的,这样就能确定范围了。
跑四遍即可,分别是最大最小,以及最左最右。
但是如果有相同的怎么办呢,必须规定唯一一个可以影响的。对于某个区间,多个最大最小值,只有最左边的有效。也就是说从左到右,相同的我们需要留下,从右到左,因为最左留下,右边的相同都是没有价值的,需要弹出。
#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define RFOR(i,a,b) for(int i=a;i>=b;i--)
#define sf(x) scanf("%d",&x)
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1000000+500;
const ll mod = 1e9+7;
int n,a[maxn];
int Maxl[maxn],Maxr[maxn];
int Minl[maxn],Minr[maxn];
deque<pair<int,int> > q[2];
int main(){
int n;cin>>n;
FOR(i,1,n)sf(a[i]);
q[0].push_back(make_pair(inf,0));
q[1].push_back(make_pair(-inf,0));
FOR(i,1,n){
while(q[0].back().first<a[i])q[0].pop_back();
Maxl[i]=q[0].back().second+1;
while(q[1].back().first>a[i])q[1].pop_back();
Minl[i]=q[1].back().second+1;
q[0].push_back(make_pair(a[i],i));
q[1].push_back(make_pair(a[i],i));
}
while(!q[0].empty())q[0].pop_back();
while(!q[1].empty())q[1].pop_back();
q[0].push_back(make_pair(inf,n+1));
q[1].push_back(make_pair(-inf,n+1));
RFOR(i,n,1){
while(q[0].back().first<=a[i])q[0].pop_back();
Maxr[i]=q[0].back().second-1;
while(q[1].back().first>=a[i])q[1].pop_back();
Minr[i]=q[1].back().second-1;
q[0].push_back(make_pair(a[i],i));
q[1].push_back(make_pair(a[i],i));
}
ll ans=0;
for(int i=1;i<=n;i++){
// cout<<Maxl[i]<<" "<<Maxr[i]<<endl;
// cout<<Minl[i]<<" "<<Minr[i]<<endl;
ans+=1ll*a[i]*(i-Maxl[i]+1)*(Maxr[i]-i+1);
ans-=1ll*a[i]*(i-Minl[i]+1)*(Minr[i]-i+1);
}
cout<<ans<<endl;
}
2100 H
题意
三个操作,加入一个数
x
x
x,删除一个数
x
x
x,询问有多少个数满足
x
⊕
y
≤
z
x \oplus y \leq z
x⊕y≤z。
给出的是
y
、
z
y、z
y、z。
题解
首先因为是不等号,不能进行正常能进行的异或运算。
首先前两个操作暗示了
T
r
i
e
Trie
Trie树,具体操作是什么呢?
逐位判断,比如说
z
z
z的第
k
k
k位是
0
0
0,那么此时查询的时候,就顺着
0
0
0往下走,此时不能确定当前的数是多是少。
但是如果是
1
1
1,当前如果有
1
1
1,那么肯定往
1
1
1走,如果有
0
0
0,能保证
0
0
0往下的都会小与
z
z
z,直接统计。
因为添加删除的时候都是直接在链上操作,所以没有问题,除了初始节点,每一条边的指向都对应一个点。
即解。
#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define sf(x) scanf("%d",&x)
using namespace std;
typedef long long ll;
const int maxn = 100000*30+500;
const ll mod = 1e9+7;
int val[maxn];
int ch[maxn][2];
struct Trie{
int sz;
void init(){
sz=1;
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
}
void insert(int x){
int u=0;
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
if(!ch[u][c]){
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
val[u]++;
}
}
void del(int x){
int u=0;
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
if(!ch[u][c])return ;
u=ch[u][c];
--val[u];
}
}
int query(int x,int y){
int u=0,ret=0;
for(int i=30;i>=0;i--){
int c=(x>>i)&1,now=(y>>i)&1;
if(now==0){
if(ch[u][c])u=ch[u][c];
else return ret;
}
else{
if(ch[u][c])ret+=val[ch[u][c]];
if(ch[u][c^1])u=ch[u][c^1];
else return ret;
}
}
return ret;
}
}trie;
int main(){
trie.init();
int n;cin>>n;
for(int i=1;i<=n;i++){
int op,p,l;sf(op);
if(op==1){
sf(p);
trie.insert(p);
}
else if(op==2){
sf(p);
trie.del(p);
}
else{
sf(p);sf(l);
int ans=trie.query(p,l);
printf("%d\n",ans);
}
}
}