XJOI 38 感想

第二题:对于一个元素集合最大值减最小值小于等于某个值可以用单调队列来维护
第三题:
给你一张图,500000组询问询问x到y之间是否存在一条经过奇数个点的路径
这道题利用了点双连通分量的性质: 一个点双连通分量如果有一个奇环,那么你如果经过其中两个不同点就一定可以改变路径的奇偶性,这个证明比较玄学,我也不确定,我是利用一个存在奇环的点双连通分量中的一个点一定在一个奇环上来证的.(雾);奇环用二分图染色来求
然后zrf给出了一种比较好的方法来建树,对于每一个点双新建一个节点与点双中的所有点连边,这样就可以方便的判断点的出入情况了.
然而我利用dfs树也给出了一种做法,我们说其具备改变奇偶的能力,当且仅当其和其父亲均在一个有奇环的点双中,这样做能够保证不是只仅仅经过一个点.(一定有一个进点一个出点).做法比较容易.
这类构造体往往与联通分量有关,下次看到改变奇偶性要想到奇环,还有zrf做法适应普遍题目,我的算法只是一个trick.

%:pragma GCC optimize(4)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
using namespace std;

const int N = 1000005;
const int M = N * 2;

int fir[N] , ne[M] , to[M] , cnt = 1  , scc_no[N] , stk[N] , tot , depth[N] , vis[N] , dfs_clock , dfn[N] , low[N] , cut[N] , scc_cnt , col[N];
int n , m  ,q , x , y , is_two[N] , can[N] , valid[N][26] , fa[N] , st[N][26];
void add(int x , int y) {
    ne[++ cnt] = fir[x]; fir[x] = cnt; to[cnt] = y;
}

void link(int x, int y) {
    add(x , y); add(y , x);
}

#define Foreachson(i  , x) for(int i = fir[x];i;i = ne[i])
#define Pair pair<int,int>

void DFS(int x, int f) {
    depth[x] = depth[f] + 1;
    vis[x] = 1;
    fa[x] = f;
    Foreachson(i , x) {
        int V = to[i];
        if(vis[V]) continue;
        DFS(V , x);
    }
}

vector<int>scc_col[N];
vector<Pair>scc_edge[N];
vector<int>G[N];

stack<Pair>S;

void dfs(int x , int f) {
    low[x] = dfn[x] = ++ dfs_clock;
    int py= 0;
    Foreachson(i , x) {
        int V = to[i];
        py ++;
        if(!dfn[V]) {
            S.push(make_pair(x , V));
            dfs(V , x);
            if(dfn[x] <= low[V]) {
                cut[x] = 1;
                ++ scc_cnt; scc_col[scc_cnt].clear();
                for(;;) {
                    Pair ind = S.top(); S.pop();
                    scc_edge[scc_cnt].push_back(ind);
                    int u = ind.first , v = ind.second;
                    if(scc_no[u] != scc_cnt) {
                        scc_no[u] = scc_cnt;
                        scc_col[scc_cnt].push_back(u);
                    }
                    if(scc_no[v] != scc_cnt) {
                        scc_no[v] = scc_cnt;
                        scc_col[scc_cnt].push_back(v);
                    }
                    if(ind == (make_pair(x , V))) break;
                }
            }
            low[x] = min(low[x] , low[V]);
        }
        else if(dfn[V] < dfn[x] && V != f) {
            low[x] = min(low[x] , low[V]);
            S.push(make_pair(V , x));
        }
    }
//  if(py == 1 && x== 1) cut[x] = 0;
}

int getans(int x , int Col) {
    col[x] = Col;
    for(int i = 0;i <(int)G[x].size();i ++) {
        int V = G[x][i];
        if(col[V] == Col) return 0;
        if(col[V] == (Col ^ 1)) continue;
        if(!getans(V , Col ^ 1)) return 0;
    }
    return 1;
}

void calc(int x) {
    for(int i = 0;i <(int) scc_col[x].size();i ++) {
        int it = scc_col[x][i];
        col[it] = x * 2 - 1;
        G[it].clear();
    }
    for(int i = 0;i <(int) scc_edge[x].size();i ++) {
        Pair now = scc_edge[x][i];
        G[now.first].push_back(now.second);
        G[now.second].push_back(now.first);
    }
    for(int i = 0;i <(int) scc_col[x].size();i ++) {
        int it = scc_col[x][i];
        if(col[it] != x * 2 && col[it] != x * 2 + 1) {
            int xxx = getans(it , x * 2);
            if(!xxx) {
                is_two[x] = 0;
                return;
            }
        }
    }
    is_two[x] = 1;
}

void getit(void) {
    for(int i = 1;i <= scc_cnt;i ++) {
        calc(i);
        if(!is_two[i]) {
            for(int j = 0;j <(int) scc_col[i].size();j ++) {
                can[scc_col[i][j]] = 1;
            }
        }
    }
}

void dfs2(int x , int f) {
    valid[x][0] = can[x] & can[f];
    st[x][0] = f;
    for(int i = 1;i <= 25;i ++) {
        st[x][i] = st[st[x][i - 1]][i - 1];
        valid[x][i] = valid[x][i - 1] | valid[st[x][i - 1]][i - 1];
    }
    Foreachson(i , x) {
        int V = to[i];
        if(fa[V] == x) {
            dfs2(V , x);
        }
    }
}

bool get(int x , int y) {
    if(depth[x] > depth[y]) swap(x , y);
    int ans = 0;
    for(int i = 25;i >= 0;i --) {
        if(depth[st[y][i]] >= depth[x]) {
            ans |= valid[y][i];
            y = st[y][i];
        }
    }
    if(x == y)  return ans;
    for(int i = 25;i >= 0;i --) if(st[x][i] != st[y][i]) {
        ans |= valid[x][i];
        ans |= valid[y][i];
        x = st[x][i];
        y = st[y][i];
    }
    ans |= valid[y][0];
    ans |= valid[x][0];
    return ans;
}

int main(void) {
    scanf("%d%d%d",&n,&m,&q);
    for(int i = 1;i <= m;i ++) {
        scanf("%d%d",&x,&y); link(x , y);
    }
    DFS(1 , 0);
    dfs(1 , 0);
    getit();
    dfs2(1 , 0);
    for(int i = 1;i <= q;i ++) {
        scanf("%d%d",&x,&y);
        if((depth[x] - depth[y]) % 2 == 0 && !get(x , y)) puts("NIE");
        else puts("TAK");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值