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. http状态码 400-499

    类比 服务器:便利店 客户端:客人 http报文:中文语言+钱 400-499 客户的错误 400 :服务器不理解客服端请求的意思是什么,如请求报文损坏 举例: 客户端:@#!3&* 服务器: ...

  2. spring-boot-plus集成Spring Boot Admin管理和监控应用

    Spring Boot Admin Spring Boot Admin用来管理和监控Spring Boot应用程序 应用程序向我们的Spring Boot Admin Client注册(通过HTTP) ...

  3. Is it a full physical image???

    My friend asked me why she could not find some important files in a physical image acquired from an ...

  4. Zabbix-agentd错误整理

    一.无法启动 (一).当时环境 Firewalld与Selinux,Iptables都为关闭 配置环境 OS:CentOS Zabbix-server IP:10.18.43.71 Hostname: ...

  5. 优雅的对象转换解决方案-MapStruct使用进阶(二)

    在前面, 介绍了 MapStruct 及其入门. 本文则是进一步的进阶. 在 MapStruct 生成对应的实现类的时候, 有如下的几个情景. 1 属性名称相同,则进行转化 在实现类的时候, 如果属性 ...

  6. (一)Mybatis基本配置,Statement方式,动态代理增删改查

    首先明白Mybatis是干什么的,之前使用jdbc操作数据库时候要写很多语句,获取光标,连接,获取具体对象进行相应操作,代码过于繁琐,所以现在有了Mybatis,它将这个操作整合在了一起,你不需要关心 ...

  7. oracle的自增序列

    因为oracle中的自增序列与mysql数据库是不一样的,所以在这里唠嗑一下oracle的自增序列 1. 创建和修改自增序列 --创建序列的语法 -- create sequence [user.]s ...

  8. 使用 Docker 生成 Let’s Encrypt 证书

    概念 什么是 Container ? https://www.docker.com/resources/what-container https://www.docker.com/why-docker ...

  9. idea使用大全(加载mysql驱动)

    1.载入mysql驱动 找到项目结构(project structure) 选Modules 找到右边的加号选择第一个 OK

  10. iview中page组件的跳转功能BUG解决方案

    xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! 在 ...