[Comet OJ Contest 8][二维数点+扫描线]菜菜种菜

题面

[题目描述]

菜菜太菜,但他不想种菜。
n n n 块土地,每块土地有一个菜值。它们之间有 m m m 条小路,每条连接两块土地,小路只能单向通行,不存在一条小路两端连接同一块土地,但可能存在两条小路两端连接的土地分别相同。如果存在一条从土地 u u u 到土地 v v v 的小路,则称 u u u 能直接到达 v v v
菜菜可以购买一些土地,他必须在其中选择一块建造自己的家,所购买的剩下的土地都被作为菜地。因为菜菜不想种菜,所以他希望从他家能直接到达的土地中,一块菜地也没有(如果菜菜家不能直接到达任何一块土地,这也能满足他的愿望)。
菜菜提出了 q q q 个问题,每个问题给出 L L L, R R R,询问如果他购买了第 L L L 到第 R R R 块之间的所有土地,那么所有满足他愿望的建造家的选址的菜值之和是多少?

注意,本题空间限制为128MB

[输入格式]

1 1 1 3 3 3 个由空格隔开的整数 n n n, m m m, q q q ,分别表示土地的块数、小路的条数和问题的个数。
2 2 2 n n n 个由空格隔开的整数,第 i i i 个数 a i a_i ai表示第 i i i 块土地的菜值。
接下来 m m m 行,每行 2 2 2 个由空格隔开整数 u i , v i u_i,v_i ui,vi,表示第 i i i 条小路从第 u i u_i ui块土地通向第 v i v_i vi块土地。
接下来 q q q 行,每行 2 2 2 个由空格隔开整数 l i l_i li, r i r_i ri,表示菜菜第 i 个问题中购买了 [ l i , r i ] [l_i,r_i] [li,ri]中的所有土地。
1 ≤ n , m , q ≤ 1 0 6 1\le n,m,q \le 10^6 1n,m,q106
1 ≤ a i ≤ 300 1\le a_i\le 300 1ai300
1 ≤ u i , v i ≤ n , u i ≠ v i 1\le u_i,v_i\le n,u_i\neq v_i 1ui,vin,ui=vi
1 ≤ l i ≤ r i ≤ n 1\le l_i\le r_i\le n 1lirin

[输出格式]

为了避免输出量过大,设 a n s i ans_i ansi表示第 i i i 个询问的答案。你只需要输出1行1个整数 x o r i = 1 q i × a n s i {\rm xor}_{i=1}^qi\times ans_i xori=1qi×ansi,即所有询问的编号与答案的乘积依次按位异或 (c++中的64位整数^)的结果。

[样例1输入]
4 2 2
3 5 10 6
1 4
2 3
2 3
1 3
[样例1输出]
16
[样例2输入]
5 6 3
114 29 219 231 165
5 4
1 2
1 3
5 3
3 2
3 4
2 2
1 2
4 5

[样例2输出]
658

题解

转化问题

首先我们考虑最暴力的做法,我们可以枚举 ∀ x , x ∈ [ L , R ] \forall x,x\in [L,R] x,x[L,R]中的所有 x x x,我们只需要检验这个 x x x是否合法即可。
一个点 x x x合法,当且仅当不存在 y ∈ [ L , R ] y\in [L,R] y[L,R]使得 x , y x,y x,y之间有一条连边。
注意到我们的询问是一个区间,我们可以发现如果区间包含了能够被 x x x到达的编号小于 x x x且与 x x x差距最小的点和编号大于 x x x且与 x x x差距最小的点这两个点的任何一个点,那么这个点就不合法,反之,当前的 x x x就合法。
因此定义 p r e x pre_x prex为编号小于 x x x且与 x x x差距最小的点, n x t x nxt_x nxtx为编号大于 x x x且与 x x x差距最小的点,如果只要以下条件 x x x就合法:
p r e x < L ≤ x 且 x ≤ R < n x t x pre_x<L≤x且x≤R<nxt_x prex<LxxR<nxtx
然后套路就来了,我们将区间看做一个点 ( L , R ) (L,R) (L,R),将 x x x看做 ( p r e x + 1 , x , x , n x t x − 1 ) (pre_x+1,x,x,nxt_x-1) (prex+1,x,x,nxtx1)(对应 ( x 0 , y 0 , x 1 , y 1 ) (x_0,y_0,x_1,y_1) (x0,y0,x1,y1))的一个矩形。
因此问题就转化为:统计一个点 ( L , R ) (L,R) (L,R)被多少个矩形包围。

扫描线

考虑使用扫描线来解决这个问题。
我们将矩阵拆为 ( x 0 , y 0 , y 1 ) , ( x 1 + 1 , y 0 , y 1 ) (x_0,y_0,y_1),(x_1+1,y_0,y_1) (x0,y0,y1),(x1+1,y0,y1)两条与y轴平行的直线,将点与直线混在一起按x轴排序
维护一个可以区间加减的数据结构,在第一条直线时往区间 ( y 0 , y 1 ) (y_0,y_1) (y0,y1)加1,在第二条直线时往区间 ( y 0 , y 1 ) (y_0,y_1) (y0,y1)减1。
对于每个询问,我们只需要单点查询当前数据结构中 x x x点的值即可。
我们用数状数组就能够完成这个工作。
时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)

实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define MAXN 1000002
#define MAXM 3000002
#define LL long long
int n,m,q,cnt,sum;
int val[MAXN+5],ans[MAXN+5],C[MAXN+5];
int Cl[MAXN+5],Cr[MAXN+5];
LL T;
struct point{
	int kdf,x,l,r,val;
}p[MAXM+5];
int read(){
	int x=0,F=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*F;
}
int lowbit(int x){return x&(-x);}
bool cmp(point s1,point s2){
	if(s1.x==s2.x)return s1.kdf>s2.kdf;
	return s1.x<s2.x;
}
void Insert(int x,int val){
	while(x>0){
		C[x]+=val;
		x-=lowbit(x);
	}
}
int Query(int x){
	int res=0;
	while(x<=MAXN){
		res+=C[x];
		x+=lowbit(x);
	}return res;
}
int main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++){
		val[i]=read();
		Cl[i]=0;Cr[i]=n+1;
	}
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		if(v<u)Cl[u]=max(Cl[u],v);
		if(v>u)Cr[u]=min(Cr[u],v);
	}
	for(int i=1;i<=q;i++){
		int l=read(),r=read();
		p[++cnt]=(point){0,l,r,0,i};
	}
	for(int i=1;i<=n;i++){
		p[++cnt]=(point){1,Cl[i]+1,i,Cr[i]-1,val[i]};
		p[++cnt]=(point){1,i+1,i,Cr[i]-1,-val[i]};
	}
	sort(p+1,p+cnt+1,cmp);
	for(int i=1;i<=cnt;i++){
		if(!p[i].kdf){
			ans[p[i].val]=Query(p[i].l);
		}else{
			Insert(p[i].r,p[i].val);
			if(p[i].l>1)Insert(p[i].l-1,-p[i].val);
		}
	}
	for(int i=1;i<=q;i++)
		T^=(1LL*i*ans[i]);
	printf("%lld",T);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值