组队传球(点双 边双)

问题描述

操场上有n名足球队员,编号1到n。
有的队员是好友关系,队员们只愿意把球传给自己的好友。总共有m对好友关系。
何老板要大家组队玩一种传球游戏,组队的要求是:参加游戏的队员中,任意一对队员间最多能有一次传球,且队员传出去的球没有机会再次传回自己。
何老板向你提出询问:在编号为[L,R]区间中,存在多少个区间[X,Y],有L<=X,Y<=R,并且编号在区间[X,Y]中的队员满足组队的条件。

输入格式
第一行,两个整数n和m
接下来m行,每行两个整数a,b,表示编号为a,b的两个队员是好友
接下来一行,一个整数q,表示何老板共提出了q次询问
接下来q行,每行两个整数L,R,表示一次询问的区间。

输出格式

q行,每行一个整数,表示答案。

解:
恶补了一波tarjan 以前只会用tarjan求强联通分量 这次涨见识了
定义d[i]表示 i 号点最远能到达的点的编号
注意到这是一个单增的函数 容易想到利用二分查找
主要在于求环

~割点就是将点删掉图不联通
~桥就是将边删掉图不联通
~点双联通分量就是将图中存在任意删掉一个点图仍旧是联通的
~边双联通分量就是将图中存在任意删掉一个边图仍旧是联通的

此题的坑点在于 点可能是割点 处于两个环之中
所以我就会想到利用tarjan 求简单环
也就是满足条件 low[u]<=low[v] v是u的父亲 那么v就是割点 只要依次弹栈 记录最大最小
对于一个环内的最大最小的情况 (a,b) 令对于x<=a 的节点 d[x]<b 此时只需要记录一下后缀和就行了
然后找一找 第一个d[x]>R 的情况
code: 不知道为啥只有90

//
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<stack>
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define maxnn 700000
int mark[maxnn];
int cnt;
int n,m;
ll rrr[maxnn];
ll dfn[maxnn],low[maxnn],insta[maxnn];
int las[maxnn],en[maxnn],nex[maxnn],tot;
int root,vis;
void add(int a,int b) {
    en[++tot]=b;
    nex[tot]=las[a];
    las[a]=tot;
}
#define GC getchar ()
inline int R() {
    char t;
    int  x=0;
    int  f=1;
    t=GC;
    while(!isdigit(t)) {
        if(t=='-') f=-1;
        t=GC;
    }
    while(isdigit(t)) {
        x=x*10+t-'0';
        t=GC;
    }
    return x*f;
}
stack<int> W;
ll f[maxnn];
int scc=0;
ll sum[maxnn];
int ttt[maxnn];
int ma[maxnn],mi[maxnn];
void tarjan(int v,int fa) {
    int r;
    mark[v]=1;
    dfn[v]=low[v]=++vis;
    W.push(v);
    for(int i=las[v]; i; i=nex[i]) {
        int u=en[i];
        int d;
        if(u!=fa) {
            if(!dfn[u]) {
                tarjan(u,v);
                low[v]=min(low[v],low[u]);
                if(dfn[v]<=low[u]) {
                    scc++;
                    do {
                        rrr[scc]++;
                        mi[scc]=min(mi[scc],W.top());
                        ma[scc]=max(ma[scc],W.top());
                        d=W.top();
                        W.pop();
                    } while(d!=v);
                    W.push(v);
                }
            } else {
                low[v]=min(low[v],dfn[u]);
            }
        }
    }
}
int main() {
    
    int x,y;
    int le,re;
    memset(mi,0x7f,sizeof(mi));
    n=R();
    m=R();
    for(int i=1; i<=m; i++) {
        x=R();
        y=R();
        add(x,y);
        add(y,x);
    }
    for(int i=1; i<=n; i++) {
        while(W.size())W.pop();
        if(!mark[i]) tarjan(i,i);
        f[i]=n;
    }
    for(int i=1; i<=scc; i++) {
        if(rrr[i]>2)
        f[mi[i]]=min(f[mi[i]],1LL*(ma[i]-1));
    }
    for(int i=1; i<=n; i++)
    {
        if(f[i]==i) f[i]=n;
    }
    for(int i=n; i>=1; i--) {
        if(f[i]==0)f[i]=10000000;
        if(f[i+1]==0)f[i+1]=10000000;
        f[i]=min(f[i],f[i+1]);
    }
    for(int i=1; i<=n; i++) {
        sum[i]=sum[i-1]+f[i]-i+1;
    }
    int q;
    q=R();
    while(q--) {
        le=R();
        re=R();
        long long  m=upper_bound(f+le,f+re+1,re)-f;
        m--;
        long long  ans=0;
        ans+=sum[m]-sum[le-1];
        ans+=((re-m)*(re+1)-((re-m)*(re+m+1))/2);
        printf("%lld
",ans);
    }
}
原文地址:https://www.cnblogs.com/OIEREDSION/p/11741984.html