• 【洛谷3776】[APIO2017] 斑斓之地(平面图欧拉公式)


    点此看题面

    • 给定一张(A imes B)的网格图,把其中一条长度为(n)的路径标为黑色(可能自交),其余点为白色。
    • (q)次询问,每次求一个子矩阵中白色连通块的数量。
    • (nle10^5,A,Ble2 imes10^5)

    平面图欧拉公式

    公式如下:

    [V-E+F=2 ]

    其中(V)为点数,(E)为边数,(F)为面数。

    由于(F)中的面数是包含外面的,因此我们可以设(F')表示图形内部的面数,得到(V-E+F'=1)

    对于树的问题我们常常利用(V-E=1)得出点数-边数=连通块数,其实就是(F'=0)的特殊情况。

    对于这道题,在相邻的网格之间连边显然得到的是一张平面图,受树情况的启发就有结论:连通块数=点数-边数+面数。

    贡献讨论+二维数点

    由于白点数量很大,而黑点数量很少,这也就意味着不合法情况数很少,因此无论点数、边数、面数,我们都考虑用总情况数减去不合法情况数。

    对于点数,非常简单,就是用((x_2-x_1+1) imes(y_2-y_1+1))减去子矩形((x_1,y_1)-(x_2,y_2))中的黑点数。

    对于边数,分成上下连边和左右连边两部分,考虑把贡献记在上方/左方的点上,那么就分别为((x_2-x_1) imes(y_2-y_1+1))减去子矩形((x_1,y_1)-(x_2-1,y_2))的贡献以及((x_2-x_1+1) imes(y_2-y_1))减去子矩形((x_1,y_1)-(x_2,y_2-1))的贡献。

    对于面数,一种情况是一组(2 imes2)的四个白点间的面,把贡献记在左上方的点上,那么就是((x_2-x_1) imes(y_2-y_1))减去子矩形((x_1,y_1)-(x_2-1,y_2-1))的贡献;另一种情况是给定的子矩形完全包含了黑色路径且最外围连通,这样一来黑色路径围出的这一整部分也成为了外围连通块的一个面,这种情况只需将黑色路径的最小/最大横纵坐标与(x_1,y_1,x_2,y_2)比较一下即可判断。

    不同的贡献计算都有一个共通的询问子矩形贡献和,就是一个二维数点问题,预处理建出主席树即可。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define M 200000
    #define pb push_back
    using namespace std;
    int A,B,n,Rt[M+5][4];vector<int> p[M+5][4];
    class ChairmanTree
    {
    	private:
    		#define PT CI l=0,CI r=B
    		#define LT l,mid
    		#define RT mid+1,r
    		int Nt;struct node {int V,S[2];}O[N*72];
    	public:
    		I void U(int& rt,CI x,PT)//单点修改
    		{
    			if(++(O[++Nt]=O[rt]).V,rt=Nt,l==r) return;RI mid=l+r>>1;
    			x<=mid?U(O[rt].S[0],x,LT):U(O[rt].S[1],x,RT);
    		}
    		I int Q(CI rt,CI L,CI R,PT)//区间求和
    		{
    			if(!rt||L<=l&&r<=R) return O[rt].V;RI mid=l+r>>1;
    			return (L<=mid?Q(O[rt].S[0],L,R,LT):0)+(R>mid?Q(O[rt].S[1],L,R,RT):0);
    		}
    }C[4];
    int main()
    {
    	#define Mark(x,y) (xl=min(xl,x),xr=max(xr,x),yl=min(yl,y),yr=max(yr,y),
    		p[x][0].pb(y),p[x-1][1].pb(y),p[x][1].pb(y),p[x][2].pb(y-1),p[x][2].pb(y),
    		p[x-1][3].pb(y-1),p[x-1][3].pb(y),p[x][3].pb(y-1),p[x][3].pb(y))
    	RI Qt,x,y,xl=1e9,xr=0,yl=1e9,yr=0;scanf("%d%d%d%d%d%d",&A,&B,&n,&Qt,&x,&y),Mark(x,y);
    	RI i;char op;for(i=1;i<=n;++i) cin>>op,op=='N'?--x:(op=='S'?++x:(op=='W'?--y:++y)),Mark(x,y);
    	RI j,k,sz;for(i=1;i<=A;++i) for(j=0;j^4;++j)
    	{
    		sort(p[i][j].begin(),p[i][j].end()),sz=unique(p[i][j].begin(),p[i][j].end())-p[i][j].begin();//去重
    		for(Rt[i][j]=Rt[i-1][j],k=0;k^sz;++k) C[j].U(Rt[i][j],p[i][j][k]);//主席树上修改
    	}
    	#define Qry(op,x1,y1,x2,y2) (C[op].Q(Rt[x2][op],y1,y2)-C[op].Q(Rt[x1-1][op],y1,y2))//询问子矩形贡献和
    	long long t;W(Qt--) scanf("%d%d%d%d",&i,&j,&x,&y),t=1LL*(x-i+1)*(y-j+1)-Qry(0,i,j,x,y),//点数
    		i^x&&(t-=1LL*(x-i)*(y-j+1)-Qry(1,i,j,x-1,y)),j^y&&(t-=1LL*(x-i+1)*(y-j)-Qry(2,i,j,x,y-1)),//边数
    		i^x&&j^y&&(t+=1LL*(x-i)*(y-j)-Qry(3,i,j,x-1,y-1)),i<xl&&xr<x&&j<yl&&yr<y&&++t,printf("%lld
    ",t);//面数
    	return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    LeetCode38 报数
    序列化和反序列化
    JAVA 正则表达式
    Comparable接口和Comparator接口
    IO流-输入输出的简单实例
    JAVA File类
    URI, URL, URN
    web自动化测试第2步:定位元素
    web自动化测试第1步:配置基于python语言的自动化测试环境
    使用webdriver扒取网站小说(一)-----基础篇
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3776.html
Copyright © 2020-2023  润新知