Nasta Rabbara

题意:简单来说就是, 现在有 n个点, m条边, 每次询问一个区间[ l ,  r ], 将这个区间的所有边都连上, 如果现在的图中有奇数环, 就输出 “Impossible”, 否者就输出 ”possible“。

题解:

步骤1:我们先找出每个最小的 [ l,  r]  当这个区间的边都出现后, 就会出现一个奇数环。

步骤2:问题就变成了对于一次询问 [ L, R ]  是否存在上面的一个区间 被完全覆盖。

对于步骤1来说:需要加边和删边, 我们用 lct 维护。

我们按照 1 ... m 的顺序, 进行添加边。

如果 u 和 v 不联通, 那么我们直接将u和v连起来。

如果 u 和 v 联通, 那么如果我们加上这个边之后就会形成环。

如果是偶数环, 那么我们就删除这个环上最先添加进来的边, 因为我们需要找到最小的[l, r]奇数环区间。

如果是奇数环, 那么说明我们已经找到了一个奇数环区间, 因为有偶数环删除的保证, 所以我们找到的一定是最小的奇数环区间。

然后我们再删除边,将 l+1前面的边都删除, 继续往下找下一个最小奇数环区间。

对于步骤2来说:

我们可以离线所有询问。

对于所有的最小奇数环区间和询问区间都按照左端点大的排序。

当询问区间的 左端点的位置 <= 当前奇数环区间的时候,就在标记一下奇数环区间的右端, 用树状数组维护。

然后查询询问区间的右端点之前有没有点出现过, 如果有就说明有区间被完全覆盖。

因为添加到树状数组里面的 奇数环区间 的左端点一定是 大或等于 当前区间左端点的。

最后输出答案。

代码:

 #include<bits/stdc++.h>
using namespace std;
#define Fopen freopen("2.in","r",stdin); freopen("_out.txt","w",stdout);
#define LL long long
#define ULL unsigned LL
#define fi first
#define se second
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lch(x) tr[x].son[0]
#define rch(x) tr[x].son[1]
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = (int)1e9+;
const int N = 2e5 + ;
struct Node{
int rev, rt;
int son[], pre;
int id, mn, sz;
void init(int t){
sz = rt = ;
rev = pre = son[] = son[] = ;
id = mn = t;
}
}tr[N];
void Push_Rev(int x){
if(!x) return ;
swap(lch(x), rch(x));
tr[x].rev ^= ;
}
void Push_Up(int x){
if(!x) return ;
tr[x].sz = tr[lch(x)].sz + tr[rch(x)].sz + ;
tr[x].mn = min3(tr[lch(x)].mn, tr[rch(x)].mn, tr[x].id);
}
void Push_Down(int x){
if(tr[x].rev){
tr[x].rev = ;
Push_Rev(lch(x));
Push_Rev(rch(x));
}
}
void Rev(int x){
if(!tr[x].rt) Rev(tr[x].pre);
Push_Down(x);
}
void rotate(int x){
if(tr[x].rt) return;
int y = tr[x].pre, z = tr[y].pre;
int k = (rch(y) == x);
tr[y].son[k] = tr[x].son[k^];
tr[tr[y].son[k]].pre = y;
tr[x].son[k^] = y;
tr[y].pre = x;
tr[x].pre = z;
if(tr[y].rt) tr[y].rt = , tr[x].rt = ;
else tr[z].son[rch(z) == y] = x;
Push_Up(y);
}
void Splay(int x){
Rev(x);
while(!tr[x].rt){
int y = tr[x].pre, z = tr[y].pre;
if(!tr[y].rt){
if(( x == rch(y) ) != (y == rch(z))) rotate(y);
else rotate(x);
}
rotate(x);
}
Push_Up(x);
}
void Access(int x){
int y = ;
do{
Splay(x);
tr[rch(x)].rt = ;
rch(x) = y;
tr[y].rt = ;
Push_Up(x);
y = x;
x = tr[x].pre;
}while(x);
}
void Make_rt(int x){
Access(x);
Splay(x);
Push_Rev(x);
}
void link(int u, int v){
Make_rt(u);
tr[u].pre = v;
}
void cut(int u, int v){
Make_rt(u);
Access(v);
Splay(v);
tr[lch(v)].pre = ;
tr[lch(v)].rt = ;
tr[v].pre = ;
lch(v) = ;
}
bool judge(int u, int v){
while(tr[u].pre) u = tr[u].pre;
while(tr[v].pre) v = tr[v].pre;
return u == v;
}
int x[N], y[N];
int l[N], r[N];
int in[N];
int ans[N];
int tot = ;
int n, m, q;
struct node{
int l, r, id;
bool operator < (const node & x) const {
return l > x.l;
}
}A[N];
void solve(int st, int ed){
for(int i = st; i <= ed; i++){
if(!in[i]) continue;
cut(x[i], i+n);
cut(y[i], i+n);
in[i] = ;
}
}
int tree[N];
inline int lowbit(int x){
return x & (-x);
}
int Query(int x){
int ret = ;
while(x){
ret += tree[x];
x -= lowbit(x);
}
return ret;
}
void Add(int x){
while(x <= m){
tree[x]++;
x += lowbit(x);
}
}
int main(){
scanf("%d%d%d", &n, &m, &q);
for(int i = ; i <= n; i++) tr[i].init(inf);
tr[].mn = tr[].id = inf;
for(int i = ; i <= m; i++){
scanf("%d%d", &x[i], &y[i]);
tr[i+n].init(i+n);
}
int b = ;
for(int i = ; i <= m; i++){
int u = x[i], v = y[i], id = n+i;
if(!judge(u, v)){
link(u, id);
link(v, id);
in[i] = ;
}
else {
Make_rt(u);
Access(v);
Splay(v);
int sz = tr[v].sz/;
int t = tr[v].mn;
if(sz&) {
cut(u, t);
cut(v, t);
in[t-n] = ;
}
else {
l[++tot] = t-n; r[tot] = i;
solve(b, t-n);
b = t-n;
}
link(u, id);
link(v, id);
in[i] = ;
}
}
for(int i = ; i <= q; i++){
scanf("%d%d", &A[i].l, &A[i].r);
A[i].id = i;
}
sort(A+, A++q);
for(int i = ; i <= q; i++){
int ll = A[i].l, rr = A[i].r, id = A[i].id;
while(tot && ll <= l[tot]){
Add(r[tot]);
tot--;
}
if(Query(rr)) ans[id] = ;
}
for(int i = ; i <= q; i++){
if(ans[i]) puts("Impossible");
else puts("Possible");
}
return ;
}

因为一直在实验室问步骤2的问题, 又听到一个别的思路。

因为我们保证了最小奇数环区间是没有覆盖情况的, 我们按照左端点小的排序。

对于每次询问我们找到第一段左端点大于询问区间的左端点的区间, 然后判断一下右端点是不是在这个区间里面就好了。

代码:

 #include<bits/stdc++.h>
using namespace std;
#define Fopen freopen("2.in","r",stdin); freopen("_out.txt","w",stdout);
#define LL long long
#define ULL unsigned LL
#define fi first
#define se second
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lch(x) tr[x].son[0]
#define rch(x) tr[x].son[1]
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = (int)1e9+;
const int N = 2e5 + ;
struct Node{
int rev, rt;
int son[], pre;
int id, mn, sz;
void init(int t){
sz = rt = ;
rev = pre = son[] = son[] = ;
id = mn = t;
}
}tr[N];
void Push_Rev(int x){
if(!x) return ;
swap(lch(x), rch(x));
tr[x].rev ^= ;
}
void Push_Up(int x){
if(!x) return ;
tr[x].sz = tr[lch(x)].sz + tr[rch(x)].sz + ;
tr[x].mn = min3(tr[lch(x)].mn, tr[rch(x)].mn, tr[x].id);
}
void Push_Down(int x){
if(tr[x].rev){
tr[x].rev = ;
Push_Rev(lch(x));
Push_Rev(rch(x));
}
}
void Rev(int x){
if(!tr[x].rt) Rev(tr[x].pre);
Push_Down(x);
}
void rotate(int x){
if(tr[x].rt) return;
int y = tr[x].pre, z = tr[y].pre;
int k = (rch(y) == x);
tr[y].son[k] = tr[x].son[k^];
tr[tr[y].son[k]].pre = y;
tr[x].son[k^] = y;
tr[y].pre = x;
tr[x].pre = z;
if(tr[y].rt) tr[y].rt = , tr[x].rt = ;
else tr[z].son[rch(z) == y] = x;
Push_Up(y);
}
void Splay(int x){
Rev(x);
while(!tr[x].rt){
int y = tr[x].pre, z = tr[y].pre;
if(!tr[y].rt){
if(( x == rch(y) ) != (y == rch(z))) rotate(y);
else rotate(x);
}
rotate(x);
}
Push_Up(x);
}
void Access(int x){
int y = ;
do{
Splay(x);
tr[rch(x)].rt = ;
rch(x) = y;
tr[y].rt = ;
Push_Up(x);
y = x;
x = tr[x].pre;
}while(x);
}
void Make_rt(int x){
Access(x);
Splay(x);
Push_Rev(x);
}
void link(int u, int v){
Make_rt(u);
tr[u].pre = v;
}
void cut(int u, int v){
Make_rt(u);
Access(v);
Splay(v);
tr[lch(v)].pre = ;
tr[lch(v)].rt = ;
tr[v].pre = ;
lch(v) = ;
}
bool judge(int u, int v){
while(tr[u].pre) u = tr[u].pre;
while(tr[v].pre) v = tr[v].pre;
return u == v;
}
int x[N], y[N];
int in[N];
int ans[N];
int tot = ;
pll P[N];
int n, m, q;
void solve(int st, int ed){
for(int i = st; i <= ed; i++){
if(!in[i]) continue;
cut(x[i], i+n);
cut(y[i], i+n);
in[i] = ;
}
}
int main(){
scanf("%d%d%d", &n, &m, &q);
for(int i = ; i <= n; i++) tr[i].init(inf);
tr[].mn = tr[].id = inf;
for(int i = ; i <= m; i++){
scanf("%d%d", &x[i], &y[i]);
tr[i+n].init(i+n);
}
int b = ;
for(int i = ; i <= m; i++){
int u = x[i], v = y[i], id = n+i;
if(!judge(u, v)){
link(u, id);
link(v, id);
in[i] = ;
}
else {
Make_rt(u);
Access(v);
Splay(v);
int sz = tr[v].sz/;
int t = tr[v].mn;
if(sz&) {
cut(u, t);
cut(v, t);
in[t-n] = ;
}
else {
P[tot].fi = t-n; P[tot++].se = i; solve(b, t-n);
b = t-n;
}
link(u, id);
link(v, id);
in[i] = ;
}
}
int l, r;
for(int i = ; i <= q; i++){
scanf("%d%d", &l, &r);
int p = upper_bound(P, P+tot, pll(l,-)) - P;
if(p == tot || r < P[p].se) puts("Possible");
else puts("Impossible");
}
return ;
}

CodeForces gym Nasta Rabbara lct的更多相关文章

  1. Codeforces Gym 101252D&&floyd判圈算法学习笔记

    一句话题意:x0=1,xi+1=(Axi+xi%B)%C,如果x序列中存在最早的两个相同的元素,输出第二次出现的位置,若在2e7内无解则输出-1. 题解:都不到100天就AFO了才来学这floyd判圈 ...

  2. Codeforces Gym 101190M Mole Tunnels - 费用流

    题目传送门 传送门 题目大意 $m$只鼹鼠有$n$个巢穴,$n - 1$条长度为$1$的通道将它们连通且第$i(i > 1)$个巢穴与第$\left\lfloor \frac{i}{2}\rig ...

  3. Codeforces Gym 101623A - 动态规划

    题目传送门 传送门 题目大意 给定一个长度为$n$的序列,要求划分成最少的段数,然后将这些段排序使得新序列单调不减. 考虑将相邻的相等的数缩成一个数. 假设没有分成了$n$段,考虑最少能够减少多少划分 ...

  4. 【Codeforces Gym 100725K】Key Insertion

    Codeforces Gym 100725K 题意:给定一个初始全0的序列,然后给\(n\)个查询,每一次调用\(Insert(L_i,i)\),其中\(Insert(L,K)\)表示在第L位插入K, ...

  5. Codeforces gym 101343 J.Husam and the Broken Present 2【状压dp】

     2017 JUST Programming Contest 2.0 题目链接:Codeforces gym 101343 J.Husam and the Broken Present 2 J. Hu ...

  6. codeforces gym 100553I

    codeforces gym 100553I solution 令a[i]表示位置i的船的编号 研究可以发现,应是从中间开始,往两边跳.... 于是就是一个点往两边的最长下降子序列之和减一 魔改树状数 ...

  7. CodeForces Gym 100213F Counterfeit Money

    CodeForces Gym题目页面传送门 有\(1\)个\(n1\times m1\)的字符矩阵\(a\)和\(1\)个\(n2\times m2\)的字符矩阵\(b\),求\(a,b\)的最大公共 ...

  8. Codeforces GYM 100876 J - Buying roads 题解

    Codeforces GYM 100876 J - Buying roads 题解 才不是因为有了图床来测试一下呢,哼( 题意 给你\(N\)个点,\(M\)条带权边的无向图,选出\(K\)条边,使得 ...

  9. codeforces Gym 100187J J. Deck Shuffling dfs

    J. Deck Shuffling Time Limit: 2   Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100187/pro ...

随机推荐

  1. TCP queue 的一些问题

    转自Jasey Wang的blog,原文地址 首先回顾下三次握手里面涉及到的问题: 当 client 通过 connect 向 server 发出 SYN 包时,client 会维护一个 socket ...

  2. DVWA-SQL注入

    SQL注入解题思路 寻找注入点,可以通过web扫描工具实现 通过注入点,尝试得到连接数据库的用户名,数据库名称,权限等信息. 猜解关键数据库表极其重要字段与内容. 通过获得的用户信息寻找后台进行登录. ...

  3. 微信公众号接入服务器验证(Go实现)

    1 基本流程 将token.timestamp.nonce三个参数进行字典序排序 将三个参数字符串拼接成一个字符串进行sha1加密 开发者获得加密后的字符串可与signature对比,标识该请求来源于 ...

  4. Hyper-v设置linux固定ip

    一.创建CentOS 7专用的虚拟交换机 打开Hyper-v控制面板,找到右边的“虚拟交换机管理器” 进去后,点击“新建虚拟网络交换机”,填写名称后,选择“内部” 打开网络中心,修改配置如下图,注意i ...

  5. Spring 集成Kafka(完整版)

    前面的文章我们已经完成了Kafka基于Zookeeper的集群的搭建了.Kafka集群搭建请点我.记过几天的研究已经实现Spring的集成了.本文重点 jar包准备 集成是基于spring-integ ...

  6. vue+el-menu实现路由刷新和导航栏菜单状态保持(局部刷新页面)

    一.菜单项激活状态保持 有时,我们在项目中会有这样一个需求,即实现 一个侧导航栏,点击不同的菜单项,右边内容会跟着变化,而页面手动刷新后想要使菜单激活状态保持,那么这个功能该如何实现呢? 现在给出以下 ...

  7. 三步理解--门控循环单元(GRU),TensorFlow实现

    1. 什么是GRU 在循环神经⽹络中的梯度计算⽅法中,我们发现,当时间步数较⼤或者时间步较小时,循环神经⽹络的梯度较容易出现衰减或爆炸.虽然裁剪梯度可以应对梯度爆炸,但⽆法解决梯度衰减的问题.通常由于 ...

  8. 构建企业级数据湖?Azure Data Lake Storage Gen2不容错过(上)

    背景 相较传统的重量级OLAP数据仓库,“数据湖”以其数据体量大.综合成本低.支持非结构化数据.查询灵活多变等特点,受到越来越多企业的青睐,逐渐成为了现代数据平台的核心和架构范式. 数据湖的核心功能, ...

  9. Sqlserver 查询把多行内容拼成一个字符串

    当使用:SELECT ','+Id FROM dbo.Test FOR XML PATH('')); //这样读取的数据虽然是1,2,3,4,但是仍然是xml格式,所以当数据超过2033时候,用sql ...

  10. Go_ go mod 命令解决墙的问题

    简介 由于众所周知的原因,在下载一些库的时候会下载不了,比如 golang.org/x/... 相关的库.为此,网上出现了很多解决方案. 从 Go1.11 开始,Go 引入了 module,对包进行管 ...