codeforces 811E Vladik and Entertaining Flags

题面

\(n*m(1<=n<=10, 1<=m<=1e5)\)的棋盘,每个格子有一个值。

定义联通块:联通块中所有格子的值相等,并且格子四联通。

\(1e5\)次询问,每次询问子矩形\((1, l, n, r)\)中联通块的数量。

题解

线段树区间合并。

\(cnt[rt]\):区间中联通块个数

\(pre[rt][]\):在区间中用并查集维护端点的连通性。在给合并之后的集合重新编号时,要注意并查集是用集合中某个点的标号表示整个集合的标号,不能直接用离散化的方法重命名。

upd

建一棵线段树,每个节点存l列到r列的信息(联通块个数,l列和r列上每个格子属于哪个联通块)。

合并的时候其实是将标号合并起来。

http://acm.hs97.cn/article/1873

代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define sz(x) (int)x.size()
#define de(x) cout<< #x<<" = "<<x<<endl
#define dd(x) cout<< #x<<" = "<<x<<" "
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
//------ const int N=11, M=101010;
int n,m,q,mrt,cntn;
int a[N][M];
int cnt[M*4+77], pre[M*4+77][N<<2]; int find(int rt,int x) {
if(pre[rt][x]==x) return x;
return pre[rt][x]=find(rt,pre[rt][x]);
}
void join(int rt,int x,int y) {
int fx=find(rt,x);
int fy=find(rt,y);
pre[rt][fx]=fy;
}
void up(int rt,int mid,int L,int R) {
rep(i,0,n) {
pre[rt][i]=pre[L][i];
pre[rt][i+n]=pre[L][i+n];
pre[rt][i+2*n]=pre[R][i]+2*n;
pre[rt][i+3*n]=pre[R][i+n]+2*n;
}
cnt[rt]=cnt[L]+cnt[R];
rep(i,0,n) if(a[i][mid]==a[i][mid+1]&&find(rt, i+n)!=find(rt, i+2*n)) join(rt, i+n, i+2*n), --cnt[rt];
int vis[N<<2]={0};
rep(i,0,n) {
int t;
t=find(rt, i);
if(!vis[t]) vis[t]=i+1;
t=find(rt, i+3*n);
if(!vis[t]) vis[t]=i+n+1;
}
rep(i,0,n) {
pre[rt][i]=vis[pre[rt][i]]-1;
pre[rt][i+n]=vis[pre[rt][i+3*n]]-1;
}
}
void build(int l,int r,int rt) {
if(rt>mrt) mrt=rt;
if(l==r) {
cnt[rt]=n;
rep(i,0,n) pre[rt][i]=i;
rep(i,1,n) if(a[i][l]==a[i-1][l]) join(rt, i, i-1), --cnt[rt];
rep(i,0,n) pre[rt][i+n]=pre[rt][i];
return ;
}
int mid=l+r>>1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
up(rt, mid, rt<<1, rt<<1|1);
}
int qry(int L,int R,int l,int r,int rt) {
if(L<=l&&r<=R) return rt;
int mid=l+r>>1;
int ll=-1, rr=-1;
if(L<=mid) ll=qry(L,R,l,mid,rt<<1);
if(R>=mid+1) rr=qry(L,R,mid+1,r,rt<<1|1);
if(ll==-1) return rr;
if(rr==-1) return ll;
++cntn;
up(cntn,mid,ll,rr);
return cntn;
} int main() {
while(~scanf("%d%d%d",&n,&m,&q)) {
///read
rep(i,0,n) rep(j,0,m) scanf("%d",&a[i][j]);
///solve
build(0, m-1, 1);
while(q--) {
int l,r;scanf("%d%d",&l,&r);
--l;--r;
cntn=mrt;
int p=qry(l,r,0,m-1,1);
printf("%d\n",cnt[p]);
}
return 0;
}
return 0;
}

upd 代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define sz(a) (int)a.size()
#define de(a) cout << #a << " = " << a << endl
#define dd(a) cout << #a << " = " << a << " "
#define all(a) a.begin(), a.end()
#define endl "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
//--- const int N = 101010; int n, m, q, top, ans, sz;
int a[11][N], pre[11*N], sta[11*N];
bool vis[11*N];
pii res[N]; inline void init() {
rep(i, 1, top+1) vis[sta[i]] = 0, pre[sta[i]] = sta[i];
top = ans = 0;
sz = 0;
} inline void push(int x) {
vis[x] = 1;
sta[++top] = x;
} int find(int x) {
if(x == pre[x]) return x;
if(!vis[x]) push(x);
return pre[x] = find(pre[x]);
} struct Seg {
#define ls rt<<1
#define rs ls|1
static const int N = ::N << 2;
int cnt[N], id[N][22];
inline void init(int rt, int l) {
cnt[rt] = 0;
rep(i, 0, n) {
if(i == 0 || a[i][l] != a[i-1][l]) {
++cnt[rt];
id[rt][i] = i * m + l;
} else {
id[rt][i] = id[rt][i-1];
}
id[rt][i+n] = id[rt][i];
}
}
inline void up(int rt, int l, int r, int mid) {
cnt[rt] = cnt[ls] + cnt[rs];
rep(i, 0, n) if(a[i][mid] == a[i][mid+1]) {
int x = find(id[ls][i+n]), y = find(id[rs][i]);
if(x == y) continue;
pre[x] = y;
if(!vis[x]) push(x);
--cnt[rt];
}
rep(i, 0, n) {
id[rt][i] = find(id[ls][i]);
id[rt][i+n] = find(id[rs][i+n]);
}
}
void build(int l, int r, int rt) {
if(l == r) {
init(rt, l);
return ;
}
int mid = l + r >> 1;
build(l, mid, ls);
build(mid+1, r, rs);
up(rt, l, r, mid);
}
void qry(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
ans += cnt[rt];
res[sz++] = mp(l, rt);
res[sz++] = mp(r, rt);
return ;
}
int mid = l + r >> 1;
if(L <= mid) qry(L, R, l, mid, ls);
if(R >= mid+1) qry(L, R, mid+1, r, rs);
}
}seg; int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
cin >> n >> m >> q;
rep(i, 0, n) rep(j, 0, m) cin >> a[i][j];
rep(i, 0, n*m) pre[i] = i;
seg.build(0, m-1, 1);
while(q--) {
int l, r;
cin >> l >> r;
--l, --r;
init();
seg.qry(l, r, 0, m-1, 1);
for(int t = 1; t < sz - 1; t += 2) {
int ll = res[t].se, rr = res[t+1].se, mid = res[t].fi;
rep(i, 0, n) if(a[i][mid] == a[i][mid+1]) {
int x = find(seg.id[ll][i+n]), y = find(seg.id[rr][i]);
if(x == y) continue;
pre[x] = y;
--ans;
if(!vis[x]) push(x);
}
}
cout << ans << endl;
}
return 0;
}

codeforces 811E Vladik and Entertaining Flags(线段树+并查集)的更多相关文章

  1. 【Codeforces811E】Vladik and Entertaining Flags [线段树][并查集]

    Vladik and Entertaining Flags Time Limit: 20 Sec  Memory Limit: 512 MB Description n * m的矩形,每个格子上有一个 ...

  2. 2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集)

    2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集) https://www.luogu.com.cn/problem/CF811E Ste ...

  3. [WC2005]双面棋盘(线段树+并查集)

    线段树+并查集维护连通性. 好像 \(700ms\) 的时限把我的常数超级大的做法卡掉了, 必须要开 \(O_2\) 才行. 对于线段树的每一个结点都开左边的并查集,右边的并查集,然后合并. \(Co ...

  4. codeforces 811 E. Vladik and Entertaining Flags(线段树+并查集)

    题目链接:http://codeforces.com/contest/811/problem/E 题意:给定一个行数为10 列数10w的矩阵,每个方块是一个整数, 给定l和r 求范围内的联通块数量 所 ...

  5. 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status ...

  6. 【XSY2707】snow 线段树 并查集

    题目描述 有\(n\)个人和一条长度为\(t\)的线段,每个人还有一个工作范围(是一个区间).最开始整条线段都是白的.定义每个人的工作长度是这个人的工作范围中白色部分的长度(会随着线段改变而改变).每 ...

  7. bzoj 2054: 疯狂的馒头(线段树||并查集)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2054 线段树写法: 点的颜色只取决于最后一次染的颜色,所以我们可以倒着维护,如果当前区间之前 ...

  8. ZOJ 4100 浙江省第16届大学生程序设计竞赛 A题 Vertices in the Pocket 线段树+并查集

    正赛的时候完全没看这个题,事后winterzz告诉我他想出来的解法. 首先题意是给出n个点,m次操作. 操作有一种是连接两个点,另一种是求此时再为这个图连k条边,最少和最多能有几个联通块. 最少的求法 ...

  9. 【CF687D】Dividing Kingdom II 线段树+并查集

    [CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两 ...

随机推荐

  1. To B服务想做移动化?腾讯云案例了解一下

    本文由 腾讯云助手团队 发布于 腾讯云云+社区 | 导语:腾讯云那么多资源和服务,就不能手机管理吗? 能. 当用户在使用To B服务时,往往会遇到各种各样的限制: 1.操作难度:涉及各种权限.多重验证 ...

  2. iOS 真机测试错误“The application could not be verified”

    iOS 真机测试错误"The application could not be verified" 真机测试的时候报错:"The application could no ...

  3. 使用jQuery实时监听input输入值的变化

    //jQuery实时监听input值变化 $("#email").on("input propertychange",function(){ var str = ...

  4. JDK1.7新特性(3):java语言动态性之脚本语言API

    简要描述:其实在jdk1.6中就引入了支持脚本语言的API.这使得java能够很轻松的调用其他脚本语言.具体API的使用参考下面的代码: package com.rampage.jdk7.chapte ...

  5. ios手势识别代理

    之前做优质派时写了个仿网易新闻导航的第三方,由于当时做项目时这个主控制器就是RootViewController,虽然用的是ScrollView但也没考虑到导航栏的手势返回的问题 ,现在做小区宝3.0 ...

  6. [转]How to get return values and output values from a stored procedure with EF Core?

    本文转自:https://stackoverflow.com/questions/43935345/how-to-get-return-values-and-output-values-from-a- ...

  7. 基于struts2注解@action的@Result跳转问题——跳转到另一个action

    初学ssh 基于注解的方式简单灵活,但是做一个例子的时候,添加用户AddUser 完成后 想页面跳转到 ListUser 这个action, 然后action 成功后 会跳转到list.jsp 显示 ...

  8. 七、并发容器ConcurrentHashMap

    一.简介 我们知道,HashMap是线程不安全的.而HashTable是线程安全的,但是JDK已经不建议使用HashTable,它已经被作为废除的实现. 在JDK并发包里面,ConcurrentHas ...

  9. JAVA基础之——JDK分析io、nio

    在哪儿:jdk\jre\lib\rt.jar package java.io;   package java.nio; 1 分类 1.1 IO 持久化序列化对象并压缩步骤 new FileOutput ...

  10. HTML绝对路径和相对路径

    HTML路径: 绝对路径:从根目录开始 相对路径:../ 相对于html文件,上一级 ./ 相对于html文件,当前路径(可以省略) 文件夹名 相对于html文件,往文件里面走