20220711模拟赛
作者:互联网
F:
题意:给定n个不大于c的正整数a1~an和m组询问,每次问 [l,r] 中有多少个数出现正偶数次。
注:强制在线
一道分块题,思路类似区间分块9(众数)那道题,连续块的答案可以用暴力来维护,每次询问时散点枚举,其对答案的贡献显然。
点击查看代码
#include<bits/stdc++.h>
#define get(i) (((i-1)/len)+1)
#define get_l(i) ((i-1)*len+1)
#define get_r(i) (min(n,len*i))
#define M 100005
#define N 350
using namespace std;
int n,c,m,a[M],s[N][M],f[N][N],t[M],len;
int main(){
scanf("%d%d%d",&n,&c,&m);len=sqrt(n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1,tmp;i<=get(n);++i){
tmp=0;
for(int j=1;j<=c;++j) s[i][j]=s[i-1][j],t[j]=0;
for(int j=i;j<=get(n);++j){
for(int k=get_l(j);k<=get_r(j);++k){
if(i==j) ++s[i][a[k]];
if(t[a[k]]&1) ++tmp;
else if(t[a[k]]) --tmp;
++t[a[k]];
}
f[i][j]=tmp;
}
}
int l,r,ans,lt;
while(m--){
scanf("%d%d",&l,&r);
l+=lt,l%=n,l++;r+=lt,r%=n,r++;
if(l>r) swap(l,r);
if(get(l)==get(r)){
ans=0;
for(int i=l;i<=r;++i) t[a[i]]=0;
for(int i=l;i<=r;++i){
if(t[a[i]]&1) ++ans;
else if(t[a[i]]) --ans;
++t[a[i]];
}
printf("%d\n",lt=ans);
continue;
}
ans=f[get(l)+1][get(r)-1];
for(int i=l;i<=get_r(get(l));++i) t[a[i]]=s[get(r)-1][a[i]]-s[get(l)][a[i]];
for(int i=get_l(get(r));i<=r;++i) t[a[i]]=s[get(r)-1][a[i]]-s[get(l)][a[i]];
for(int i=l;i<=get_r(get(l));++i){
if(t[a[i]]&1) ++ans;
else if(t[a[i]]) --ans;
++t[a[i]];
}
for(int i=get_l(get(r));i<=r;++i){
if(t[a[i]]&1) ++ans;
else if(t[a[i]]) --ans;
++t[a[i]];
}
printf("%d\n",lt=ans);
}
return 0;
}
H:
题意:给出一个大小为n的有根树,每个点有一个值a[i]。
定义一条路径的靠谱度为路径上所有ai的和。
定义路径的长度为路径上点的数量。
定义路径上的点为b1,b2,b3......(b[i]和b[i+1]有一条边),则b[i]为b[i+1]的父亲。
要求找出m条互不相同的路径,每条路径的长度为[L,R],使这些路径的靠谱值最大。
求最大的靠谱值之和。
首先我们先尝试一下本题的简化版:超级钢琴
对于这道题目可以把一个枚举左端点,而右端点是一段区间,区间最值易得。
所以我们可以把一个三元组放到堆里,其中表示左端点以及这个区间,
排列顺序为后面的区间最值与左端点权值之差,每次取出来之后再把这个区间从最值点劈开,把剩下两个区间与该节点的组成的三元组继续放到堆里。
点击查看代码
#include<bits/stdc++.h>
#define M 500005
using namespace std;
int n,k,L,R;
long long sm[M];
struct node{
long long bei_zeng[M][35];
void init(){
for(int i=1;i<=n;++i) bei_zeng[i][0]=i;
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i+(1<<j)-1<=n;++i){
int x=bei_zeng[i][j-1],y=bei_zeng[i+(1<<(j-1))][j-1];
bei_zeng[i][j]=sm[x]>sm[y]?x:y;
}
}
int query(int l,int r){
int k=log2(r-l+1);
int x=bei_zeng[l][k],y=bei_zeng[r-(1<<k)+1][k];
return sm[x]>sm[y]?x:y;
}
}tr;
struct E{
int o,l,r;
};
bool operator < (E a,E b){
return sm[tr.query(a.l,a.r)]-sm[a.o-1]<sm[tr.query(b.l,b.r)]-sm[b.o-1];
}
priority_queue<E> q;
int main(){
scanf("%d%d%d%d",&n,&k,&L,&R);
for(int i=1;i<=n;++i){
scanf("%lld",&sm[i]);
sm[i]+=sm[i-1];
}
tr.init();
for(int i=1;i<=n;++i)
if(i+L-1<=n)
q.push(E{i,i+L-1,min(i+R-1,n)});
long long ans=0;
while(k--){
int o=q.top().o,l=q.top().l,r=q.top().r,t;
t=tr.query(l,r);
q.pop();
ans+=sm[t]-sm[o-1];
if(l!=t) q.push(E{o,l,t-1});
if(r!=t) q.push(E{o,t+1,r});
}
printf("%lld",ans);
return 0;
}
其实是一个道理。用一个点向上倍增,找到那个合法的[L,R]区间,之后用前缀和数组差分维护区间之和,依旧用RMQ处理三元组,并且每一次找最大,分裂后再放回。
点击查看代码
#include<bits/stdc++.h>
#define M 500005
using namespace std;
int n,f[M][20],dep[M],sm[M][20],m,L,R,lg[M];
long long a[M],ans;
int find(int p,int k){
for(;k;k-=(k&(-k))) p=f[p][__builtin_ctz(k)];
return p;
}
void init(){
for(int i=1;i<=n;++i)
for(int j=1;(1<<j)<=n;++j)
f[i][j]=f[f[i][j-1]][j-1],
sm[i][j]=a[sm[i][j-1]]<a[sm[f[i][j-1]][j-1]]?sm[i][j-1]:sm[f[i][j-1]][j-1];
}
int query(int l,int r){
int k=lg[dep[l]-dep[r]+1];
int x=sm[l][k],y=sm[find(l,dep[l]-dep[r]+1-(1<<k))][k];
return a[x]<a[y]?x:y;
}
struct node{
int o,l,r,t;
};
bool operator < (node qwe,node ewq){
return a[qwe.t]-a[qwe.o]>a[ewq.t]-a[ewq.o];
}
priority_queue<node> q;
int main(){
scanf("%d",&n);
for(int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;++i){scanf("%d",&f[i][0]);dep[i]=dep[f[i][0]]+1;sm[i][0]=i;}
for(int i=1;i<=n;++i){scanf("%lld",&a[i]);a[i]+=a[f[i][0]];}
scanf("%d%d%d",&m,&L,&R);
init();
for(int i=1;i<=n;++i)
if(dep[i]>=L){ int l=find(i,L);int r=find(i,R);/*printf("%d %d %d %d\n",i,l,r,query(l,r));*/q.push(node{i,l,r,query(l,r)});}
while(m--){
int o=q.top().o,l=q.top().l,r=q.top().r,t=q.top().t;
q.pop();
ans+=a[o]-a[t];
int nw_l=l,nw_r=find(o,dep[o]-dep[t]-1);
if(t!=l) q.push(node{o,nw_l,nw_r,query(nw_l,nw_r)});
nw_l=f[t][0];nw_r=r;
if(t!=r) q.push(node{o,nw_l,nw_r,query(nw_l,nw_r)});
}
printf("%lld",ans);
return 0;
}
其他题目:
A:
求极差
题解略
B:
二进制数*17后输出
从下往上进位
C:
题意:
一共有N块木板,每块长度为hi,宽度为1.
乐乐要用其中的若干块来造篱笆.
每一块篱笆,必须由两块木板连起来,宽度1,长度为两块之和,连接后插到地上.
所有篱笆必须一样高.
他想知道,最多能造几块篱笆? 在这个块数下,有几种不同高度的方案?
只需要枚举答案,然后循环得到答案。
D:
题意:
FJ的n头奶牛现在都在栅栏旁,它们排成一条直线,每头牛的位置都不同。牛的颜色有两种,一种是白色的,一种是斑点的,且至少有一头白色的牛。
FJ现在想对一些连续的牛进行拍照,他希望这个区间的白色牛和斑点牛一样多。如果这样的区间有多个,他希望最左边牛的位置和最右边牛的位置之差最大(照片可以拍到的距离最长)。
FJ发现拍照的时候,让两种牛的个数相同,是在太难了。他想通过其他的办法来实现,photoshop是个不错的选择,他可以在拍照之后,把白色牛变成斑点牛。
请你帮他算算,他可以拍到的最长距离是多少?
这样的区间满足白牛大于等于斑牛,并且二者之差为偶数;所以我们可以按前缀和的奇偶开两个树状数组,枚举右端点,寻找最小左端点。
E
一道分块题,具体怎么维护易得。
G
倍增+并查集
每一个合并区间拆成log个,然后按照区间大小依次转移
标签:20220711,int,long,区间,query,nw,模拟,define 来源: https://www.cnblogs.com/goldenwalnut/p/16469405.html