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. Linux系统命令。

      help:命令用于显示shell内部命令的帮助信息.help命令只能显示shell内部的命令              帮助信息.而对于外部命令的帮助信息只能使用man或者info命令查看   m ...

  2. python 的几种数据类型

    列表  列表是 Python  的主力数据类型.当提到 " 列表 " 时,您脑海中可 能会闪现" 必须进一步声明大小的数组,只能包含同一类对象 "  等想法.千 ...

  3. java中对事务的理解

    一.什么是事务 事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取. 二.事务的原则(ACID) 原子性:事务要么全部都被执行,要么就全都不被执行,如果有子事务提交失败,那么 ...

  4. Java课堂 动手动脑5

    1.了解棋盘打印:利用二维数组输出一个15*15的棋盘,棋盘的原素为“+”,就是输出一个横纵都是15个“+”的二维数组,然后再以坐标形式输入棋子“●”,替换掉原来棋盘里的“+”.再编写代码. 电脑随机 ...

  5. 如何成为PHP程序员?

    当今,互联网的蓬勃发展,移动互联网的火热,以及国家提出的“互联网+”.这些趋势可以让我们明显的感觉到互联网的重要,不可替代.网站也是大家最早接触,最早认识的一种新事物.谈到网站,无非最长脸的莫过于PH ...

  6. Redis的分布式和主备配置调研

    目前Redis实现集群的方法主要是采用一致性哈稀分片(Shard),将不同的key分配到不同的redis server上,达到横向扩展的目的. 对于一致性哈稀分片的算法,Jedis-2.0.0已经提供 ...

  7. struts与springmvc有何区别

    Struts2与SpringMVC有何区别? (1)SpringMVC的核心控制器是基于servlet技术,而Struts2是基于filter. (2)Struts2是类级别的拦截, 一个类对应一个r ...

  8. java中的异常 try catch

    1.学习异常的原因?      如果没有异常处理机制,那么程序的一点小问题,都会导致[程序终止运行].实际开发中显然是不可能的,所以异常对于程序来说是非常重要的.     2.处理异常的方式:   A ...

  9. cs231n---卷积网络可视化,deepdream和风格迁移

    本课介绍了近年来人们对理解卷积网络这个“黑盒子”所做的一些可视化工作,以及deepdream和风格迁移. 1 卷积网络可视化 1.1 可视化第一层的滤波器 我们把卷积网络的第一层滤波器权重进行可视化( ...

  10. Hive安装与部署

    进去root权限(su) 1.从https://mirrors.tuna.tsinghua.edu.cn/apache/hive/hive-1.2.2/apache-hive-1.2.2-bin.ta ...