2024-03-31

2024-03-31

讲课提到的

很有道理啊,确实很常见
窗口的星星里面就用到了

还有一个小技巧
求区间 0 的个数不好做
有的时候满足所有数非负
转化成求区间最小值是不是 0 和区间最小值的个数就行了

这两天讲课的时候还经常提到
· 修改和查询的复杂度不平衡的时候,把他平衡会更优秀

扫描线:将静态二维问题转化为动态一维问题

窗口的星星

每个星星可以转化为 横坐标从 x 到 x+W-1 ,纵坐标从 y 到 y+H-1 的 权值为 l 的矩形

这样我们要求的就变成了二维平面上权值最大的点

扫描线,每次区间加 取全局最大值

注意这个题和模板题求面积不同,那个题线段树维护的是切出来的线段,点是边界,这个题线段树维护的就是点,因此不用有 lft+1 之类的操作

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

#define ls (u<<1)
#define rs (u<<1|1)
#define mid (tr[u].lft+tr[u].rgh>>1)

using namespace std;

typedef long long ll; 

const int N=1e4+100;

int n;
ll W,H;

struct Seg {
	ll pos;
	ll hgh,low;
	ll wgh;
	
	bool operator <(const Seg &t)const {
		if(pos==t.pos) return wgh>t.wgh;
		return pos<t.pos;
	}
}a[N*2];
int cnt;

struct Node {
	int lft,rgh;
	ll val,tag;
}tr[N*8];

vector<ll> nums;

ll g(ll x) {
	return lower_bound(nums.begin(),nums.end(),x)-nums.begin();
}

void build(int u,int lft,int rgh) {
	tr[u].rgh=rgh,tr[u].lft=lft;
	tr[u].tag=tr[u].val=0;
	if(lft==rgh) return;
	build(ls,lft,mid),build(rs,mid+1,rgh);
}

void pushup(int u) {
	tr[u].val=max(tr[ls].val,tr[rs].val);
}

void pushdown(int u) {
	if(tr[u].tag) {
		tr[ls].val+=tr[u].tag,tr[rs].val+=tr[u].tag;
		tr[ls].tag+=tr[u].tag,tr[rs].tag+=tr[u].tag;
		tr[u].tag=0;
	}
}

void update(int u,int ul,int ur,ll ux) {
	if(tr[u].lft>=ul&&tr[u].rgh<=ur) {
		tr[u].val+=ux,tr[u].tag+=ux;
		return;
	}
	pushdown(u);
	if(ul<=mid) update(ls,ul,ur,ux);
	if(ur>mid) update(rs,ul,ur,ux);
	pushup(u);
}

int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		memset(a,0,sizeof(a));
		memset(tr,0,sizeof(tr));
		cnt=0;
		nums.clear();
		scanf("%d%lld%lld",&n,&W,&H);
		for(int i=1;i<=n;i++) {
			ll x,y,w;
			scanf("%lld%lld%lld",&x,&y,&w);
			nums.push_back(y),nums.push_back(y+H-1);
			cnt++,a[cnt].pos=x,a[cnt].low=y,a[cnt].hgh=y+H-1,a[cnt].wgh=w;
			cnt++,a[cnt].pos=x+W-1,a[cnt].low=y,a[cnt].hgh=y+H-1,a[cnt].wgh=-w;
		}
		sort(nums.begin(),nums.end());
		nums.erase(unique(nums.begin(),nums.end()),nums.end());
		sort(a+1,a+cnt+1);
		ll ans=0;
		build(1,1,nums.size());
		for(int i=1;i<=cnt;i++) {
			update(1,g(a[i].low),g(a[i].hgh),a[i].wgh);
			ans=max(ans,tr[1].val);
		}
		printf("%lld\n",ans);
	}
	
	return 0;
}

Yuno loves sqrt technology III

好像是做过的第一道 Ynoi 的题

\({\color{Chocolate}题意}\)
强制在线的区间众数的出现次数

分块

先离散化

预处理

  • f[i][j] 表示 块 i 到 块 j 的众数(用一个桶)
  • 一个 vector pos[N] 表示每种数的位置,并记录每个元素在 vector 中的下标 idx

处理询问的时候

  • 整块通过 f 数组求
  • 散块暴力扩展,一个元素 w[i]pos[w[i]][idx[i]+ans]<=qr 说明共有 ans+1 个这种数出现,答案可以加以(这是对于左边的散块来说,右边差不多)

时间复杂度 \(O(n\sqrt n)\)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>

using namespace std;

const int N=5e5+50,B=777;
const int Inf=2e9;

int n,m;
int w[N];
vector<int> nums;

vector<int> pos[N];
int idx[N];
int len,blk[N],lft[B],rgh[B];
int f[B][B];
int cnt[N];

int g(int x) {
	return lower_bound(nums.begin(),nums.end(),x)-nums.begin()+1;
}

int main() {
	scanf("%d%d",&n,&m);
	int len=sqrt(n);
	for(int i=1;i<=n;i++) blk[i]=(i-1)/len+1,lft[blk[i]]=Inf,rgh[blk[i]]=-Inf;
	for(int i=1;i<=n;i++) lft[blk[i]]=min(lft[blk[i]],i),rgh[blk[i]]=max(rgh[blk[i]],i); 
	for(int i=1;i<=n;i++) scanf("%d",&w[i]),nums.push_back(w[i]);
	sort(nums.begin(),nums.end());
	nums.erase(unique(nums.begin(),nums.end()),nums.end());
	for(int i=1;i<=n;i++) w[i]=g(w[i]);
	for(int i=1;i<=blk[n];i++) {
		for(int j=i;j<=blk[n];j++) {
			f[i][j]=f[i][j-1];
			for(int k=lft[j];k<=rgh[j];k++) f[i][j]=max(f[i][j],++cnt[w[k]]);
		}
		memset(cnt,0,sizeof(cnt));
	}
	for(int i=1;i<=n;i++) pos[w[i]].push_back(i),idx[i]=pos[w[i]].size()-1;
	int ans=0;
	while(m--) {
		int ql,qr;
		scanf("%d%d",&ql,&qr);
		ql^=ans,qr^=ans;
		ans=0;
		if(blk[ql]==blk[qr]) {
			for(int i=ql;i<=qr;i++) ans=max(ans,++cnt[w[i]]);
			for(int i=ql;i<=qr;i++) cnt[w[i]]--;
		}
		else {
			ans=f[blk[ql]+1][blk[qr]-1];
			for(int i=ql;i<=rgh[blk[ql]];i++)
				while(idx[i]+ans<pos[w[i]].size()&&pos[w[i]][idx[i]+ans]<=qr)
					ans++;
			for(int i=qr;i>=lft[blk[qr]];i--) 
				while(idx[i]-ans>=0&&pos[w[i]][idx[i]-ans]>=ql)
					ans++;
		}
		printf("%d\n",ans);
	}
	
	return 0;
}

XOR and Favorite Number

异或可以前缀和

问题转化为 \([l-1,r]\) 有多少对数 \((x,y)\) 满足 \(x\oplus y=k\)

直接莫队

cnt 记录数们出现的次数
加入的时候看有多少个 cnt[x^k] res 就加多少
删除差不多

注意 两个数异或起来可能会比原来的两个数都大,空间要开够

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

typedef long long ll;

const int N=100100,V=2000100;

int n,m,k;
int w[N];
int len;

struct Query {
	int id;
	int l,r;
	
	#define blk(x) ((x-1)/len+1)
	bool operator< (const Query &t)const {
		if(blk(l)==blk(t.l)) return r<t.r;
		return blk(l)<blk(t.l);
	}
}qry[N];

ll ans[N];
ll res;
ll cnt[V];

void add(int x) {
	res+=cnt[x^k];
	cnt[x]++;
}

void del(int x) {
	cnt[x]--;
	res-=cnt[x^k];
}

int main() {
	scanf("%d%d%d",&n,&m,&k);
	len=sqrt(n);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]),w[i]^=w[i-1];
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&qry[i].l,&qry[i].r);
		qry[i].id=i;
	}
	sort(qry+1,qry+m+1);
	int h=0,t=1;
	for(int i=1;i<=m;i++) {
		int l=qry[i].l-1,r=qry[i].r;
		while(h<r) add(w[++h]);
		while(t>l) add(w[--t]);
		while(h>r) del(w[h--]);
		while(t<l) del(w[t++]);
		ans[qry[i].id]=res;
	}
	for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
	
	return 0;
} 

教主的魔法

分块
块内维护排序后的序列,还要记录排完序之后的位置

  1. 修改:
  • 整块打标记
  • 散块暴力加,重构
  1. 查询:
  • 整块二分
  • 散块暴力统计

查询的时候记得处理 tag (散块当然也要处理)数据太弱这都能得 90 分,很久没看出来

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const int N=1e6+100,B=1010;
const int Inf=2e9;

int n,m;
struct Data {
	int val;
	int id;
	bool operator< (const Data &t)const {
		return val<t.val;
	}
}w[N];
int len,blk[N],lft[B],rgh[B],tag[B],pos[N];

void update(int l,int r,int x) {
	if(blk[l]==blk[r]) {
		for(int i=l;i<=r;i++) w[pos[i]].val+=x;
		sort(w+lft[blk[l]],w+rgh[blk[l]]+1);
		for(int i=lft[blk[l]];i<=rgh[blk[l]];i++) pos[w[i].id]=i;
		return;
	}
	for(int i=blk[l]+1;i<=blk[r]-1;i++) tag[i]+=x;
	for(int i=l;i<=rgh[blk[l]];i++) w[pos[i]].val+=x;
	sort(w+lft[blk[l]],w+rgh[blk[l]]+1);
	for(int i=lft[blk[l]];i<=rgh[blk[l]];i++) pos[w[i].id]=i;
	for(int i=lft[blk[r]];i<=r;i++) w[pos[i]].val+=x;
	sort(w+lft[blk[r]],w+rgh[blk[r]]+1);
	for(int i=lft[blk[r]];i<=rgh[blk[r]];i++) pos[w[i].id]=i;
}

int lwrbnd(int lft,int rgh,int x) {
	while(lft<rgh) {
		int mid=lft+rgh>>1;
		if(w[mid].val>=x) rgh=mid;
		else lft=mid+1;
	}
	return lft;
}

int query(int l,int r,int x) {
	int res=0;
	if(blk[l]==blk[r]) {
		for(int i=l;i<=r;i++) if(w[pos[i]].val<x-tag[blk[l]]) res++;
		return r-l+1-res;
	}
	for(int i=blk[l]+1;i<=blk[r]-1;i++) res+=lwrbnd(lft[i],rgh[i]+1,x-tag[i])-lft[i];
	for(int i=l;i<=rgh[blk[l]];i++) if(w[pos[i]].val<x-tag[blk[l]]) res++;
	for(int i=lft[blk[r]];i<=r;i++) if(w[pos[i]].val<x-tag[blk[r]]) res++;
	return r-l+1-res;
}

int main() {
	scanf("%d%d",&n,&m);
	len=sqrt(n);
	for(int i=1;i<=n;i++) blk[i]=(i-1)/len+1,lft[blk[i]]=Inf,rgh[blk[i]]=-Inf;
	for(int i=1;i<=n;i++) lft[blk[i]]=min(lft[blk[i]],i),rgh[blk[i]]=max(rgh[blk[i]],i);
	for(int i=1;i<=n;i++) scanf("%d",&w[i].val),w[i].id=i;
	for(int i=1;i<=blk[n];i++) {
		sort(w+lft[i],w+rgh[i]+1);
		for(int j=lft[i];j<=rgh[i];j++) pos[w[j].id]=j;
	}
	while(m--) {
		char opt[5];
		int l,r,x;
		scanf("%s%d%d%d",opt,&l,&r,&x);
		if(*opt=='M') update(l,r,x);
		else printf("%d\n",query(l,r,x));
	}
	
	return 0;
}

作业

莫队+值域分块

明天再写

posted @ 2024-03-31 21:33  OrangeStar*  阅读(2)  评论(0编辑  收藏  举报