【做题记录】HZOJ 多校-ds
C. [Ynoi2005] rmscne
直接做不好维护,考虑扫描线。用线段树对每个位置 \(i\) 维护 \(p_i\) 表示 \([i,p_i]\) 是 \([i,r]\) 的最小的合法子区间。维护方式很简单,当加入 \(a_r\) 时,设上一次出现的位置为 \(j\),则 \([j+1,r]\) 的 \(p\) 值应赋为 \(r\)。
考虑询问 \([l,r]\),我们要找到最大的 \(c\) 使得 \([l,c]\) 是 \([l,r]\) 的合法子区间,答案即为 \(\min_{i=l}^{c}\{p_i-i\}\)。考虑当加入 \(r\) 时,上一次出现的位置 \(j\) 就没用了,\(j\) 对应的 \(c\) 就应指向 \(j+1\),使用并查集即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=2e6+5,inf=1e9;
int n,m,a[maxn],fa[maxn],tr[maxn<<2],tag[maxn<<2],pos[maxn],ans[maxn];
vector<pii> q[maxn];
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void pushup(int id){
tr[id]=min(tr[lid],tr[rid]);
}
il void pushtag(int id,int l,int r,int x){
tag[id]=x,tr[id]=x-r+1;
}
il void pushdown(int id,int l,int r){
if(tag[id]){
int mid=(l+r)>>1;
pushtag(lid,l,mid,tag[id]);
pushtag(rid,mid+1,r,tag[id]);
tag[id]=0;
}
}
il void upd(int id,int L,int R,int l,int r,int x){
if(L>=l&&R<=r){
pushtag(id,L,R,x);
return ;
}
pushdown(id,L,R);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,x);
}
if(r>mid){
upd(rid,mid+1,R,l,r,x);
}
pushup(id);
}
il int query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
pushdown(id,L,R);
int mid=(L+R)>>1,res=inf;
if(l<=mid){
res=min(res,query(lid,L,mid,l,r));
}
if(r>mid){
res=min(res,query(rid,mid+1,R,l,r));
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
fa[i]=i;
}
cin>>m;
for(int i=1,l,r;i<=m;i++){
cin>>l>>r;
q[r].pb(mp(l,i));
}
for(int i=1;i<=n;i++){
upd(1,1,n,pos[a[i]]+1,i,i);
fa[find(pos[a[i]])]=find(pos[a[i]]+1);
pos[a[i]]=i;
for(pii t:q[i]){
int j=t.fir,id=t.sec;
ans[id]=query(1,1,n,j,find(j));
}
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
D. [Ynoi2019 模拟赛] Yuno loves sqrt technology II
假设 \(n,m\) 同阶。(实则是嫌麻烦)
这个问题显然可以莫队 + 树状数组做,莫队过程中当右端点右移时给答案加上这个区间中比 \(a_r\) 大的个数,其他类似。然而时间复杂度 \(O(n\sqrt{n}\log n)\),需要进一步优化。
莫队二次离线
顾名思义,虽然莫队已经是离线算法了,但其中的一些区间查询(如查询当前 \([l,r]\) 中有多少个数比 \(a_r\) 大)仍然需要较高复杂度,于是我们将这些查询再离线下来,用扫描线的方式解决。
回到本题,这个查询显然可以差分成 \([1,r]\) 和 \([1,l-1]\)。\([1,r]\) 可以直接使用树状数组预处理出来,不成问题;我们就将 \([1,l-1]\) 这一部分离线下来。然后对 \(l\) 进行扫描线,我们将在值域上进行 \(O(n\sqrt{n})\) 次区间查询和 \(O(n)\) 次单点修改,于是再分块即可。注意这样求出来的答案实则是相邻两个询问答案之差,需要再进行前缀和。
然而空间复杂度爆炸。考虑莫队左右端点移动的过程,每个 \(l\) 会对应一段区间的 \(r\),于是只存储这一段的左右端点即可,空间线性。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lwrb lower_bound
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5,B=316,maxb=319;
int n,m,a[maxn],lsh[maxn];
int bnm,st[maxb],ed[maxb],bel[maxn];
int pre[2][maxn],ans[maxn];
struct node{
int l,r,id,typ;
node(int l=0,int r=0,int id=0,int typ=0):l(l),r(r),id(id),typ(typ){}
il bool operator<(const node &x)const{
return bel[l]!=bel[x.l]?l<x.l:bel[l]&1?r<x.r:r>x.r;
}
}b[maxn];
vector<node> c[maxn];
struct{
#define lowbit(x) (x&-x)
int tr[maxn];
il void add(int p){
for(;p<=n;p+=lowbit(p)){
tr[p]++;
}
}
il int query(int p){
int res=0;
for(;p;p-=lowbit(p)){
res+=tr[p];
}
return res;
}
#undef lowbit
}F;
struct{
int tr[maxn],tag[maxb];
il void add(int l,int r){
if(l>r){
return ;
}
int pl=bel[l],pr=bel[r];
if(pl==pr){
for(int i=l;i<=r;i++){
tr[i]++;
}
return ;
}
for(int i=pl+1;i<pr;i++){
tag[i]++;
}
for(int i=l;i<=ed[pl];i++){
tr[i]++;
}
for(int i=st[pr];i<=r;i++){
tr[i]++;
}
}
il int query(int p){
return tr[p]+tag[bel[p]];
}
}BL[2];
il void init(){
cin>>n>>m;
int cnt=0;
for(int i=1;i<=n;i++){
// cerr<<"666\n";
cin>>a[i];
lsh[++cnt]=a[i];
}
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(int i=1;i<=n;i++){
a[i]=lwrb(lsh+1,lsh+cnt+1,a[i])-lsh;
F.add(a[i]);
pre[0][i]=pre[0][i-1]+F.query(a[i]-1);
pre[1][i]=pre[1][i-1]+F.query(n)-F.query(a[i]);
}
for(int i=1;i<=m;i++){
cin>>b[i].l>>b[i].r;
b[i].id=i;
}
bnm=(n+B-1)/B;
for(int i=1;i<=bnm;i++){
st[i]=ed[i-1]+1;
ed[i]=min(ed[i-1]+B,n);
for(int j=st[i];j<=ed[i];j++){
bel[j]=i;
}
}
}
il void work(){
sort(b+1,b+m+1);
int l=1,r=0;
for(int i=1;i<=m;i++){
if(r<b[i].r){
c[l-1].pb(node(r+1,b[i].r,-b[i].id,1));
ans[b[i].id]+=pre[1][b[i].r]-pre[1][r];
r=b[i].r;
}
if(l>b[i].l){
c[r].pb(node(b[i].l,l-1,b[i].id,0));
ans[b[i].id]-=pre[0][l-1]-pre[0][b[i].l-1];
l=b[i].l;
}
if(r>b[i].r){
c[l-1].pb(node(b[i].r+1,r,b[i].id,1));
ans[b[i].id]-=pre[1][r]-pre[1][b[i].r];
r=b[i].r;
}
if(l<b[i].l){
c[r].pb(node(l,b[i].l-1,-b[i].id,0));
ans[b[i].id]+=pre[0][b[i].l-1]-pre[0][l-1];
l=b[i].l;
}
}
// cerr<<"777\n";
}
il void solve(){
for(int i=1;i<=n;i++){
BL[0].add(a[i]+1,n),BL[1].add(1,a[i]-1);
for(node t:c[i]){
for(int j=t.l;j<=t.r;j++){
if(t.id>0){
ans[t.id]+=BL[t.typ].query(a[j]);
}else{
ans[-t.id]-=BL[t.typ].query(a[j]);
}
}
}
}
for(int i=1;i<=m;i++){
ans[b[i].id]+=ans[b[i-1].id];
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<'\n';
}
}
int main(){
// system("fc P5047_1.out my.out");
// freopen("P5047_1.in","r",stdin);
// freopen("my.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
init(),work(),solve();
return 0;
}
}
signed main(){return asbt::main();}
/*
5 3
9 3 9 4 7
1 5
2 4
3 5
*/
E. 魔法
首先考虑没有 \(k\) 的限制怎么做。如果没有 \(3\) 操作,我们可以直接用种类并查集维护,给每个枢纽开 \(5\) 个域即可。而加上三操作,相当于每个关系都有一个有效的时间,线段树分治即可。需要可撤销并查集。
那么再考虑加上 \(k\) 的限制,每次会从之前的某一个点开始,很容易想到操作树。但我们仍然希望用类似序列的方法做,于是将操作树再拍到 \(dfn\) 上即可。对于 \(1,2\) 操作直接给整个子树打上标记,对于三操作将原来的区间再拆成两个区间即可。这样做时间复杂度仍然是 \(O(n\log^2n)\) 的。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define uprb upper_bound
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define lid id<<1
#define rid id<<1|1
#define ID(x,d) (n*(d)+(x))
using namespace std;
namespace asbt{
const int maxn=1e5+5,inf=1e9;
int n,m,dfn[maxn],cnt,fa[maxn*5],sz[maxn*5],ans[maxn],Ans;
vector<int> e[maxn],tr[maxn<<2];
stack<pii> stk;
struct{
int opt,u,v;
}q[maxn];
set<pii> st[maxn];
il void dfs1(int u){
dfn[u]=++cnt,sz[u]+=1;
for(int v:e[u]){
dfs1(v),sz[u]+=sz[v];
}
}
il void dfs2(int u){
// cout<<u<<'\n';
switch(q[u].opt){
case 1:
case 2:{
st[u].insert(mp(dfn[u],dfn[u]+sz[u]-1));
break;
}case 3:{
int x=q[u].u;
auto t=prev(st[x].uprb(mp(dfn[u],inf)));
if(dfn[u]>t->fir){
st[x].insert(mp(t->fir,dfn[u]-1));
}
if(dfn[u]+sz[u]<=t->sec){
st[x].insert(mp(dfn[u]+sz[u],t->sec));
}
st[x].erase(t);
break;
}
}
for(int v:e[u]){
dfs2(v);
}
}
il int find(int x){
return x!=fa[x]?find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
swap(u,v);
}
fa[u]=v,sz[v]+=sz[u];
stk.push(mp(u,v));
}
il void chx(){
int u=stk.top().fir,v=stk.top().sec;
stk.pop(),fa[u]=u,sz[v]-=sz[u];
}
il bool chk(int x){
int f[5]={find(ID(x,0)),find(ID(x,1)),find(ID(x,2)),find(ID(x,3)),find(ID(x,4))};
sort(f,f+5);
for(int i=0;i<=3;i++){
if(f[i]==f[i+1]){
return 1;
}
}
return 0;
}
il void upd(int id,int L,int R,int l,int r,int x){
if(L>=l&&R<=r){
tr[id].pb(x);
return ;
}
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,x);
}
if(r>mid){
upd(rid,mid+1,R,l,r,x);
}
}
il void work(int id,int l,int r){
int top=stk.size();
for(int x:tr[id]){
int u=q[x].u,v=q[x].v,t=q[x].opt;
for(int i=0;i<=4;i++){
merge(ID(u,i),ID(v,(i+t)%5));
}
}
for(int x:tr[id]){
if(chk(q[x].u)||chk(q[x].v)){
Ans++;
}
}
if(l==r){
ans[l]=Ans;
}else{
int mid=(l+r)>>1;
work(lid,l,mid);
work(rid,mid+1,r);
}
for(int x:tr[id]){
if(chk(q[x].u)||chk(q[x].v)){
Ans--;
}
}
while(stk.size()>top){
chx();
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,x;i<=m;i++){
cin>>x>>q[i].opt>>q[i].u;
e[x].pb(i);
if(q[i].opt<3){
cin>>q[i].v;
}
}
// puts("666");
dfs1(0);
// puts("777");
dfs2(0);
// puts("888");
for(int i=1;i<=m;i++){
// cout<<i<<'\n';
for(pii j:st[i]){
upd(1,1,cnt,j.fir,j.sec,i);
}
}
for(int i=1;i<=n*5;i++){
fa[i]=i,sz[i]=1;
}
// puts("999");
work(1,1,cnt);
// puts("000");
for(int i=1;i<=m;i++){
cout<<(ans[dfn[i]]?"naive":"excited")<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
F. [JSOI2008] Blue Mary 开公司
每种设计方案对应一条直线,李超线段树即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n;
struct line{
double k,b;
line(double k=0,double b=0):k(k),b(b){}
il double calc(int x){
return k*x+b;
}
}tr[maxn<<1];
il void insert(int id,int l,int r,line x){
int mid=(l+r)>>1;
if(tr[id].calc(mid)<x.calc(mid)){
swap(tr[id],x);
}
double lo=tr[id].calc(l),ro=tr[id].calc(r);
double ln=x.calc(l),rn=x.calc(r);
if(ln>lo){
insert(lid,l,mid,x);
}else if(rn>ro){
insert(rid,mid+1,r,x);
}
}
il double query(int id,int l,int r,int p){
if(l==r){
return tr[id].calc(p);
}
int mid=(l+r)>>1;
if(p<=mid){
return max(tr[id].calc(p),query(lid,l,mid,p));
}else{
return max(tr[id].calc(p),query(rid,mid+1,r,p));
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
while(n--){
string opt;
cin>>opt;
if(opt[0]=='P'){
double s,p;
cin>>s>>p;
insert(1,1,5e4,line(p,s-p));
}else{
int x;
cin>>x;
cout<<(int)(query(1,1,5e4,x)/100)<<'\n';
}
}
return 0;
}
}
int main(){return asbt::main();}
G. [HEOI2016/TJOI2016] 排序
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,q,a[maxn],tr[maxn<<2][2],tag[maxn<<2];
struct{
int opt,l,r;
}b[maxn];
il void pushup(int id){
tr[id][0]=tr[lid][0]+tr[rid][0];
tr[id][1]=tr[lid][1]+tr[rid][1];
}
il void pushtag(int id,int l,int r,int x){
tr[id][x]=r-l+1,tr[id][x^1]=0;
tag[id]=x;
}
il void pushdown(int id,int l,int r){
if(~tag[id]){
int mid=(l+r)>>1;
pushtag(lid,l,mid,tag[id]);
pushtag(rid,mid+1,r,tag[id]);
tag[id]=-1;
}
}
il void build(int id,int l,int r,int x){
tag[id]=-1;
if(l==r){
tr[id][0]=tr[id][1]=0;
tr[id][a[l]>=x]=1;
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid,x);
build(rid,mid+1,r,x);
pushup(id);
}
il void upd(int id,int L,int R,int l,int r,int x){
if(l>r){
return ;
}
if(L>=l&&R<=r){
pushtag(id,L,R,x);
return ;
}
pushdown(id,L,R);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,x);
}
if(r>mid){
upd(rid,mid+1,R,l,r,x);
}
pushup(id);
}
il int query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id][0];
}
pushdown(id,L,R);
int mid=(L+R)>>1,res=0;
if(l<=mid){
res+=query(lid,L,mid,l,r);
}
if(r>mid){
res+=query(rid,mid+1,R,l,r);
}
return res;
}
il bool check(int x){
build(1,1,n,x);
for(int i=1;i<=m;i++){
// cout<<i<<'\n';
int l=b[i].l,r=b[i].r;
int c0=query(1,1,n,l,r),c1=r-l+1-c0;
// puts("666");
if(b[i].opt){
upd(1,1,n,l,l+c1-1,1);
upd(1,1,n,l+c1,r,0);
}else{
// puts("777");
upd(1,1,n,l,l+c0-1,0);
// puts("888");
upd(1,1,n,l+c0,r,1);
// puts("999");
}
}
return query(1,1,n,q,q);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i].opt>>b[i].l>>b[i].r;
}
cin>>q;
int l=1,r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)){
r=mid-1;
}else{
l=mid;
}
}
cout<<l;
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号