• CF1368G


    CF1368G - Shifting Dominoes

    题目大意

    给定一个被(1 imes 2)的骨牌(横向或者竖向)铺满的方格图

    现在可以拿走一个骨牌,之后任意一个骨牌可以沿着其放置方向左右移动至多一步

    求最终两个空位所在不同位置的方案数


    分析

    观察一个空位的移动

    如果上/下/左/右边是一条骨牌,则可以移动到该骨牌所在的上/下/左/右边方格

    将这个移动方式构成一个有向图(当然忽略反复横跳的情况)

    大胆猜测此时你会发现它是 两片外向树森林,下面说明三个充分条件

    1.跳跃的过程为((x,y) ightarrow (xpm 2,ypm 2)),显然(x+y)的奇偶性不变,故可以黑白染色分为两部分

    2.一个点至多有一条入边:一个点的入边只能来自其所在骨牌的另一边

    3.图中不存在环:

    假设构成了一个环,此时这些边对应的骨牌围成一个不规则的环

    从环的某一个角出发,向四周走,发现其余所有点总能完成一一匹配

    也就是说,环内部包含的点个数为奇数,显然不存在这样的覆盖方案


    答案计算

    考虑移除一张骨牌生成两个点((x,y)),两个点分属于两片森林,并且可以向下走

    不妨求出森林的( ext{dfs})序,此时问题变成了一个二维空间矩形覆盖问题

    可以扫描线+线段树解决

    const int N=2e5+10;
    
    int n,m,d;
    string s[N];
    int I(int x,int y){ return (x-1)*m+y; }
    ll ans;
    
    vector <int> G[N];
    int col(int u){ return ((u-1)%m+(u-1)/m)&1; }
    
    void Link(int u,int v){ G[u].pb(v),ind[v]++; }
    int ind[N],L[N],R[N],dfn;
    void dfs(int u,int f){
    	L[u]=++dfn;
    	for(int v:G[u]) if(v!=f) dfs(v,u);
    	R[u]=dfn;
    }
    
    // 线段树维护扫描过程中第二维未被覆盖的点个数
    struct Node{
    	int mi,x;
    	Node operator + (const Node _) const {
    		Node res;
    		res.mi=min(mi,_.mi),res.x=0;
    		if(mi==res.mi) res.x+=x;
    		if(_.mi==res.mi) res.x+=_.x;
    		return res;
    	}
    } tr[N<<2];
    int t[N<<2];
    void Down(int p){
    	if(!t[p]) return;
    	rep(i,p<<1,i+1) t[i]+=t[p],tr[i].mi+=t[p];
    	t[p]=0;
    }
    void Upd(int p,int l,int r,int ql,int qr,int x){
    	if(ql<=l && r<=qr) {
    		t[p]+=x,tr[p].mi+=x;
    		return;
    	}
    	Down(p);
    	int mid=(l+r)>>1;
    	if(ql<=mid) Upd(p<<1,l,mid,ql,qr,x);
    	if(qr>mid) Upd(p<<1|1,mid+1,r,ql,qr,x);
    	tr[p]=tr[p<<1]+tr[p<<1|1];
    }
    
    struct Update{
    	int p,l,r,x;
    	bool operator < (const Update __) const { return p<__.p; }
    } U[N*2];
    int C;
    
    void Add(int x,int y){
    	if(col(x)) swap(x,y);
    	U[++C]=(Update){L[x],L[y],R[y],1};
    	U[++C]=(Update){R[x]+1,L[y],R[y],-1};
    }
    void Build(int p,int l,int r){
    	tr[p]=(Node){0,r-l+1};
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	Build(p<<1,l,mid),Build(p<<1|1,mid+1,r);
    }
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,n) {
    		cin>>s[i];
    		rep(j,1,m) {
    			if(s[i][j-1]=='U') if(i>1) Link(I(i-1,j),I(i+1,j));
    			if(s[i][j-1]=='D') if(i<n) Link(I(i+1,j),I(i-1,j));
    			if(s[i][j-1]=='L') if(j>1) Link(I(i,j-1),I(i,j+1));
    			if(s[i][j-1]=='R') if(j<m) Link(I(i,j+1),I(i,j-1));
    		}
    	}
        // 获得dfs序
    	rep(i,1,n*m) if(!ind[i]) dfs(i,0);
    	rep(i,1,n) {
    		rep(j,1,m) {
    			if(s[i][j-1]=='U') Add(I(i,j),I(i+1,j));
    			if(s[i][j-1]=='L') Add(I(i,j),I(i,j+1));
    		}
    	}
    	Build(1,1,dfn);
    	sort(U+1,U+C+1);
    	int p=1;
    	rep(i,1,dfn) {
    		while(p<=C && U[p].p<=i) Upd(1,1,dfn,U[p].l,U[p].r,U[p].x),p++;
    		int c=dfn-(tr[1].mi==0?tr[1].x:0);
    		ans+=c;
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    JAVA面向对象概述
    练习
    字符串
    图形代码
    assets转到内外部存储
    file存储
    sp存储
    Intent练习
    存储登录
    存储
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14752824.html
Copyright © 2020-2023  润新知