• 组队传球(点双 边双)


    问题描述

    操场上有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);
        }
    }
    
  • 相关阅读:
    转 new和malloc的区别
    转 内联函数
    转 C++宏定义详解
    转 内联函数和宏定义的区别
    转 C++中不能声明为虚函数的有哪些函数
    转 PV操作简单理解
    转 Python执行系统命令的方法
    转 常量指针和指针常量的区别详解
    转 python语法学习面向对象之继承
    Singleton、MultiThread、Lib——实现单实例无锁多线程安全API
  • 原文地址:https://www.cnblogs.com/OIEREDSION/p/11741984.html
Copyright © 2020-2023  润新知