当前位置: 代码迷 >> 综合 >> [Comet OJ Contest 8][二维数点+扫描线]菜菜种菜
  详细解决方案

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

热度:110   发布时间:2023-10-22 21:34:50.0

题面

[题目描述]

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

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

[输入格式]

111333 个由空格隔开的整数 nnn,mmm,qqq ,分别表示土地的块数、小路的条数和问题的个数。
222nnn 个由空格隔开的整数,第 iii 个数 aia_iai?表示第 iii 块土地的菜值。
接下来mmm 行,每行222 个由空格隔开整数 ui,viu_i,v_iui?,vi?,表示第iii 条小路从第 uiu_iui?块土地通向第 viv_ivi?块土地。
接下来 qqq 行,每行 222 个由空格隔开整数 lil_ili?,rir_iri?,表示菜菜第 i 个问题中购买了 [li,ri][l_i,r_i][li?,ri?]中的所有土地。
1≤n,m,q≤1061\le n,m,q \le 10^61n,m,q106
1≤ai≤3001\le a_i\le 3001ai?300
1≤ui,vi≤n,ui≠vi1\le u_i,v_i\le n,u_i\neq v_i1ui?,vi?n,ui??=vi?
1≤li≤ri≤n1\le l_i\le r_i\le n1li?ri?n

[输出格式]

为了避免输出量过大,设 ansians_iansi?表示第 iii 个询问的答案。你只需要输出1行1个整数 xori=1qi×ansi{\rm xor}_{i=1}^qi\times ans_ixori=1q?i×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]中的所有xxx,我们只需要检验这个xxx是否合法即可。
一个点xxx合法,当且仅当不存在y∈[L,R]y\in [L,R]y[L,R]使得x,yx,yx,y之间有一条连边。
注意到我们的询问是一个区间,我们可以发现如果区间包含了能够被xxx到达的编号小于xxx且与xxx差距最小的点和编号大于xxx且与xxx差距最小的点这两个点的任何一个点,那么这个点就不合法,反之,当前的xxx就合法。
因此定义prexpre_xprex?为编号小于xxx且与xxx差距最小的点,nxtxnxt_xnxtx?为编号大于xxx且与xxx差距最小的点,如果只要以下条件xxx就合法:
prex<L≤x且x≤R<nxtxpre_x<L≤x且x≤R<nxt_x prex?<LxxR<nxtx?
然后套路就来了,我们将区间看做一个点(L,R)(L,R)(L,R),将xxx看做(prex+1,x,x,nxtx?1)(pre_x+1,x,x,nxt_x-1)(prex?+1,x,x,nxtx??1)(对应(x0,y0,x1,y1)(x_0,y_0,x_1,y_1)(x0?,y0?,x1?,y1?))的一个矩形。
因此问题就转化为:统计一个点(L,R)(L,R)(L,R)被多少个矩形包围。

扫描线

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