CWOI DS 专题 2

发布时间 2023-12-21 20:24:01作者: xx019

怎么又开一个 ds 专题啊/yun

A - 如何正确地排序

以前写的,把以前写的题解贺过来。

正难则反,总贡献减去不会成为 \(\min/\max\) 的数。 \(B_i+B_j\) 不会产生贡献的条件就是存在 \(A_i+A_j,C_i+C_j\) 满足 \(\begin{cases}A_i+A_j\le B_i+B_j\\B_i+B_j\le C_i+C_j\end{cases}\),移项可得 \(\begin{cases}A_i-B_i\le B_j-A_j\\B_i-C_i\le C_j-B_j\end{cases}\)。两边的项都只和 \(i,j\) 有关系,可以直接算出来。枚举 \(A,B,C\),这就是一个二维数点。注意相等会算两遍,我们可以钦定行数大的更大。因为有负数,要开两倍空间。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,SIZ=2e5;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct BIT{
	int c[400005];
	void clear(){memset(c,0,sizeof(c));}
	void add(int x,int k){
		for(;x<=SIZ<<1;x+=x&-x)c[x]+=k;
	}
	int ask(int x){
		int res=0;for(;x;x-=x&-x)res+=c[x];
		return res;
	}
}Tr;
struct Node{
	int op,x,y,id;
}q[400005];
int cmp(Node x,Node y){
	return (x.x!=y.x)?(x.x<y.x):(x.op<y.op);
}
int n,m,ans,a[5][200005];
void calc(int x,int y,int z){
	Tr.clear();
	for(int i=1;i<=m;i++){
		q[i]=(Node){0,(a[x][i]-a[y][i])+(x>y)+SIZ,(a[y][i]-a[z][i])+(y>z)+SIZ,i};
		q[i+m]=(Node){1,-(a[x][i]-a[y][i])+SIZ,-(a[y][i]-a[z][i])+SIZ,i};
	}
	sort(q+1,q+m*2+1,cmp);
	for(int i=1;i<=m*2;i++){
		if(q[i].op==0)Tr.add(q[i].y,1);
		else ans-=a[y][q[i].id]*Tr.ask(q[i].y);
	}
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
		a[i][j]=read(),ans+=a[i][j]*2*m;
	for(int i=n+1;i<=4;i++)for(int j=1;j<=m;j++)
		a[i][j]=a[i-n][j],ans+=a[i][j]*2*m;
	n=4;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)
		if(i!=j&&i!=k&&j!=k)calc(i,j,k);
	printf("%lld",ans);
	return 0;
}

L - Souvenirs

典,考虑所有 \(i<j,a_i\ge a_j\)\((i,j)\) 的贡献,\(a_i\le a_j\) 的贡献可以令 \(a_i\gets -a_i\) 再做一遍。枚举 \(i\),向左找到第一个 \(j\) 满足 \(a_j\ge a_i\),注意到此时一对 \((k,i),k<j<i\) 可能有用的条件是 \(a_i\le a_k\le\lfloor\dfrac{a_i+a_j}{2}\rfloor\),因为如果更靠近 \(a_j\),那么如果一个询问包含 \((k,i)\),那 \((k,j)\) 也一定可选,且一定比 \((k,i)\) 更优。如果 \(a_k>a_j\) 那更不行了。于是一个位置只会有不超过 \(\log V\) 对有用的答案,找出来之后扫描线即可,复杂度 \(\mathcal{O}(n\log n\log V)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18,V=1e9;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct segtree{
	#define ls (lc[p])
	#define rs (rc[p])
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	int T,lc[4000005],rc[4000005],mx[4000005];
	void clear(){
		for(int i=0;i<=T;i++)lc[i]=rc[i]=0,mx[i]=-inf;
		T=0;
	}
	void pushup(int p){
		mx[p]=max(mx[ls],mx[rs]);
	}
	void add(int l,int r,int &p,int x,int v){
		if(!p)p=++T,mx[p]=-inf;
		if(l==r){mx[p]=max(mx[p],v);return;}
		int mid=(l+r)>>1;
		if(x<=mid)add(lson,x,v);
		else add(rson,x,v);
		pushup(p);
	}
	int ask(int l,int r,int p,int L,int R){
		if(!p)return -inf;
		if(L<=l&&r<=R)return mx[p];
		int mid=(l+r)>>1,res=-inf;
		if(L<=mid)res=max(res,ask(lson,L,R));
		if(R>mid)res=max(res,ask(rson,L,R));
		return res;
	}
	#undef lson
	#undef rson
	#undef ls
	#undef rs
}Tr;
int a[100005],ql[300005],qr[300005],ans[300005];vector<int>v[100005],t[100005];
signed main(){
	int n=read(),root=0;
	for(int i=1;i<=n;i++)a[i]=read();
	int m=read();
	for(int i=1;i<=m;i++)ql[i]=read(),qr[i]=read(),v[qr[i]].push_back(i),ans[i]=inf;
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		int j=V;
		while(a[i]<=j){
			int p=Tr.ask(0,V,root,a[i],j);if(p<=0)break;
			t[i].push_back(p);
			if(a[p]==a[i])break;
			j=(a[i]+a[p])>>1;
		}
		Tr.add(0,V,root,a[i],i);
	}
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		for(auto x:t[i])Tr.add(1,n,root,x,a[i]-a[x]);
		for(auto x:v[i])ans[x]=min(ans[x],-Tr.ask(1,n,root,ql[x],qr[x]));
	}
	for(int i=1;i<=n;i++)a[i]=V-a[i],t[i].clear();
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		int j=V;
		while(a[i]<=j){
			int p=Tr.ask(0,V,root,a[i],j);if(p<=0)break;
			t[i].push_back(p);
			if(a[p]==a[i])break;
			j=(a[i]+a[p])>>1;
		}
		Tr.add(0,V,root,a[i],i);
	}
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		for(auto x:t[i])Tr.add(1,n,root,x,a[i]-a[x]);
		for(auto x:v[i])ans[x]=min(ans[x],-Tr.ask(1,n,root,ql[x],qr[x]));
	}
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}