CF1368G Shifting Dominoes (线段树)
题面
有一个
n
×
m
n\times m
n×m 的棋盘,被
1
×
2
1\times 2
1×2 的骨牌覆盖,保证
2
∣
n
×
m
2|n\times m
2∣n×m。
现在你需要执行以下操作:
移去恰好一个骨牌。
将其他骨牌沿着其长边进行移动。
你需要保证每张骨牌的最终位置与初始位置至少有一个交点。
求你通过若干次操作后可以得到的所有可能的局面的数量。
两种局面不同,当且仅当在其中一者中某个位置被骨牌覆盖,而另一者没有。
1
≤
n
×
m
≤
2
×
1
0
5
1\leq n\times m\leq 2\times10^5
1≤n×m≤2×105
题解
这个题要转化很久啊,
不同的局面,翻译过来就是最终两个空块的位置方案。
因此我们考虑空块的移动,如果一个多米诺骨牌的一端面向一个空块,就可以通过移动将这个空块的位置变成该骨牌的另一端,于是把它抽象成两个位置之间的一条有向边。一个骨牌可以产生两条有向边,但是每个骨牌只能移动一步,所以对于不小心使用了该骨牌两条有向边的情况,可以等价于最初移去的是该骨牌。
对于该有向图还有不错的性质。由于每次移动与上一次位置的曼哈顿距离都是 2,所以假使我们对棋盘进行黑白染色,那么最初的两个空块一定颜色不同,而且只有相同颜色的位置之间有边。我们可以发现,每个点的入度最多为 1,并且可以证明没有环:
如果存在一个环,那么(在原来的棋盘上)环的长度为 4 的倍数,除以 2 后是偶数,由该环组成的封闭图形面积也是偶数,所以由皮克公式
S
(
2
a
)
=
B
2
(
2
b
)
+
I
−
1
S(2a)=\frac{B}{2}(2b)+I-1
S(2a)=2B(2b)+I−1 可得环内的面积(
I
I
I)为奇数,但是环内不会有空块(两个空块的来源必须得是同一个骨牌),所以环内必须能放满骨牌,这与面积为奇数矛盾,因此不存在环。
所以整个有向图是森林。
一种局面可达,当且仅当这两个位置跑反图分别可达的节点中,存在一对节点是原来某个骨牌的两端。为了方便,我们规定原来某个骨牌两端的点是朋友。
我们遍历黑色点的树,然后对于每个点到根的链,我们处理出链上结点的所有朋友跑正图(往叶子方向)可达的节点数,就是这个黑点的贡献。
计算可达的节点数要去重,这不难。我们处理出白森林中每个点的 dfs 序(不同树点 dfs 序不同),放到线段树上去重就是了。
可以用可回退线段树(不好打),也可以用可持久化。
时间复杂度
O
(
n
m
log
(
n
m
)
)
O(nm\log(nm))
O(nmlog(nm)) 。
CODE
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<bitset>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define BI bitset<35>
namespace{ // 良好封装习惯,不怕重名
LL read() {
LL f=1,x=0;int s = getchar();if(s < 0) return -1;
while(s < '0' || s > '9') {if(s == '-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s^48);s = getchar();}
return f * x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar('0'+(x%10));}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) {putchar('-');x = -x;}
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
int n,m,s,o,k;
char ss[MAXN],a[MAXN];
int id[MAXN],cn,fr[MAXN];
int to[MAXN];
int I(int x,int y) {return (x-1)*m+y;}
int X(int i) {return (i-1)/m + 1;}
int Y(int i) {return (i-1)%m + 1;}
int ind[MAXN];
struct it{
int ls,rs;
int nm,lz;
it(){ls=rs=0;nm=0;lz=0;}
}tre[MAXN*64];
int CNT;
int addtree(int a,int l,int r,int al,int ar) {
if(l > r || al > r || ar < l || tre[a].lz) return a;
tre[++ CNT] = tre[a]; a = CNT;
if(al >= l && ar <= r) {tre[a].lz=1;tre[a].nm = ar-al+1;return a;}
int md = (al + ar) >> 1;
tre[a].ls = addtree(tre[a].ls,l,r,al,md);
tre[a].rs = addtree(tre[a].rs,l,r,md+1,ar);
tre[a].nm = tre[tre[a].ls].nm + tre[tre[a].rs].nm;
return a;
}
int findtree(int a,int l,int r,int al,int ar) {
if(l > r || al > r || ar < l || !a) return 0;
if(tre[a].lz) return min(ar,r) - max(l,al) + 1;
if(al >= l && ar <= r) return tre[a].nm;
int md = (al + ar) >> 1;
return findtree(tre[a].ls,l,r,al,md) + findtree(tre[a].rs,l,r,md+1,ar);
}
int hd[MAXN],nx[MAXN];
void ins(int x,int y) {nx[y] = hd[x];hd[x] = y;}
int dfn[MAXN],rr[MAXN],tim;
void dfs0(int x) {
dfn[x] = ++ tim; for(int i = hd[x];i;i = nx[i]) dfs0(i); rr[x] = tim;
}
int rt[MAXN];
LL dfs(int x,int ff) {
rt[x] = addtree(rt[ff],dfn[fr[x]],rr[fr[x]],1,tim);
LL rs = tre[rt[x]].nm;
for(int i = hd[x];i;i = nx[i]) {
rs += dfs(i,x);
}return rs;
}
}
int main() {
n = read();m = read();
for(int i = 1;i <= n;i ++) {
scanf("%s",ss + 1);
for(int j = 1;j <= m;j ++) {
a[I(i,j)] = ss[j];
if(a[I(i,j)] == 'R') id[I(i,j)] = id[I(i,j-1)] = ++ cn,fr[I(i,j)] = I(i,j-1),fr[I(i,j-1)] = I(i,j);
else if(a[I(i,j)] == 'D') id[I(i,j)] = id[I(i-1,j)] = ++ cn,fr[I(i,j)] = I(i-1,j),fr[I(i-1,j)] = I(i,j);
}
}
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
if(a[I(i,j)] == 'U') {
if(i+2 <= n) to[I(i,j)] = I(i+2,j);
}
else if(a[I(i,j)] == 'D') {
if(i-2 > 0) to[I(i,j)] = I(i-2,j);
}
else if(a[I(i,j)] == 'L') {
if(j+2 <= m) to[I(i,j)] = I(i,j+2);
}
else if(j-2 > 0) to[I(i,j)] = I(i,j-2);
}
}
for(int i = 1;i <= n*m;i ++) {
if(to[i]) ins(to[i],i);
}
for(int i = 1;i <= n*m;i ++) {
if((X(i)+Y(i)) % 2 == 0) {
if(!to[i]) dfs0(i);
}
}
LL ans = 0;
for(int i = 1;i <= n*m;i ++) {
if((X(i)+Y(i)) & 1) {
if(!to[i]) ans += dfs(i,0);
}
}
AIput(ans,'\n');
return 0;
}
CF1368G Shifting Dominoes (线段树)的更多相关文章
- bzoj3932--可持久化线段树
题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...
- codevs 1082 线段树练习 3(区间维护)
codevs 1082 线段树练习 3 时间限制: 3 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区 ...
- codevs 1576 最长上升子序列的线段树优化
题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...
- codevs 1080 线段树点修改
先来介绍一下线段树. 线段树是一个把线段,或者说一个区间储存在二叉树中.如图所示的就是一棵线段树,它维护一个区间的和. 蓝色数字的是线段树的节点在数组中的位置,它表示的区间已经在图上标出,它的值就是这 ...
- codevs 1082 线段树区间求和
codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...
- PYOJ 44. 【HNSDFZ2016 #6】可持久化线段树
#44. [HNSDFZ2016 #6]可持久化线段树 统计 描述 提交 自定义测试 题目描述 现有一序列 AA.您需要写一棵可持久化线段树,以实现如下操作: A v p x:对于版本v的序列,给 A ...
- CF719E(线段树+矩阵快速幂)
题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...
- 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序
3779: 重组病毒 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 224 Solved: 95[Submit][Status][Discuss] ...
- 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集
3673: 可持久化并查集 by zky Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 1878 Solved: 846[Submit][Status ...
随机推荐
- 掘地三尺搞定 Redis 与 MySQL 数据一致性问题
Redis 拥有高性能的数据读写功能,被我们广泛用在缓存场景,一是能提高业务系统的性能,二是为数据库抵挡了高并发的流量请求,点我 -> 解密 Redis 为什么这么快的秘密. 把 Redis 作 ...
- WAVE音频格式及及转换代码
音频信号的读写.播放及录音 python已经支持WAV格式的书写,而实时的声音输入输出需要安装pyAudio(http://people.csail.mit.edu/hubert/pyaudio).最 ...
- 29.MySQL高级SQL语句
MySQL高级SQL语句 目录 MySQL高级SQL语句 创建两个表 SELECT DISTINCT WHERE AND OR IN BETWEEN 通配符 LIKE ORDER BY 函数 数学函数 ...
- 某CMS后台通杀getshell
此CMS是基于thinkphp框架二次开发的,目前有thinkphp5,以及thinkphp6两种版本.这些漏洞挖掘出来的时候已经在cnvd被提交过了.但是网上并没有漏洞文章.避免风险这里只分享思路. ...
- 写了个基于 MacOS + iTerm2 自动打开窗口执行命令的工具
大家好,我是秋风,今天要给大家带来的这个工具是我最近写的 一个 npm 工具.mmt 是基于 MacOS + iTerm2 ,目的主要是为了提高日常生活中的效率,接下来我带大家看看一些常用的一些场景. ...
- Vue回炉重造之图片加载性能优化
前言 图片加载优化对于一个网站性能好坏起着至关重要的作用.所以我们使用Vue来操作一波.备注 以下的优化一.优化二栏目都是我自己封装在Vue的工具函数里,所以请认真看完,要不然直接复制的话,容易出错的 ...
- SAP Web Dynpro - 个性化和配置
根据业务需求,您可以实现许多标准应用程序,并且Web Dynpro应用程序的UI可以根据要求而有所不同. 应用配置 要配置Web Dynpro应用程序,首先要为单个Web Dynpro组件配置数据记录 ...
- SAP -SE30 程序运行时间分析
运行SE30 选中Program,点击Excute 点击运行 分析结果
- js 循环生成元素,并为元素添加click事件,结果只执行最后一个点击事件
问题描述:有一个参数集合data,for循环为每一个参数生成一个dom元素,并附加onclick事件.生成之后发现点击事件里的参数全是data集合里的最后一个. 代码如下: var dom=$('#d ...
- webpack中文api
1. 简介 1.Plugins://插件 webpack has a rich plugin interface.Most of the features are internal plug ...