2018.8.10T1(拆点、基环树)

描述
小A和一些朋友来到了一个热带花园游玩。花园中有 n 个喷泉(标号 0 ~ n−1 ),以及 m条双向道路。

每条道路有个互不相同的美丽程度,当一个人在节点 x 时,他会选择一条与 x 相连的最美丽的道路走过去,如果他在前一时刻是从那条道路走过来的,则他会选择美丽程度次大的道路走(如果不存在次大的,则还是走最大的那条)。保证每个点都至少有一条边与它相连。

一开始每个点上都有一个人,他们将按照上述规则行走,每单位时间所有人都走过一条道路。

在 P 号点上有一个豪华餐厅,你需要计算在 Ki 时间后恰好在 P 号点上的人数。

有 q 组 Ki 需要你计算,每组的 P 都相同,询问互相独立。

输入格式
第一行三个数 n, m, P。

接下来M行,每行两个数 x , y 表示一条道路。道路按照美丽程度从大到小给出。

接下来一行一个数 q 。

接下来一行,q 个正整数 Ki。

输出格式
一行Q个数,表示每次询问的答案。

样例1
样例输入1
5 5 2
1 0
1 2
3 2
1 3
4 2
2
3 1
样例输出1
1 2
样例2
样例输入2
6 6 0
1 2
0 1
0 3
3 4
4 5
1 5
1
3
样例输出2
2

限制与约定
对于 30% 的数据,n≤1000n≤1000, m≤10000m≤10000, Ki≤100Ki≤100。

对于 60% 的数据,q=1q=1。

对于 100% 的数据,2≤n≤150000, 1≤m≤150000 1≤q≤2000, 1≤Ki≤ 109 10 9

时间限制:1s

空间限制:512MB


可以发现每个点最大和次大会走的点是可以 O(n) O ( n ) 预处理出来的
直接做会比较麻烦 我们把点拆开做
有个很显然的性质就是一个联通块肯定是个基环内向树
那么很显然,每个点分为三种情况:
①到不了p
②仅到一次p
③到p之后沿着环走多次经过p
那我们把p分为在环上,不在环上考虑
预处理出每个点到p的距离统计答案即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(register int i = j;i <= k;++i)
#define repp(i,j,k) for(register int i = j;i >= k;--i)
#define ll long long
#define P pair<int,int>
const int INF = 0x7fffffff;
int n , m , p , Q;
int linkk[151000] , t;
int to1[151000] , to2[151000] , to[301000];
int dis[301000][3];
int ans[3][301000];
bool flag[301000][3];
int len_A , len_B , tmp;
int K;
bool vis[301000];
P q[2010];
struct node{
    int n , y , v;
}e[601000];

namespace fastIO{ 
    #define BUF_SIZE 100000 
    #define OUT_SIZE 100000 
    bool IOerror=0; 
    inline char nc(){ 
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE; 
        if (p1==pend){ 
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin); 
            if (pend==p1){IOerror=1;return -1;} 
        } 
        return *p1++; 
    } 
    inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';} 
    inline void read(int &x){ 
        bool sign=0; char ch=nc(); x=0; 
        for (;blank(ch);ch=nc()); 
        if (IOerror)return; 
        if (ch=='-')sign=1,ch=nc(); 
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; 
        if (sign)x=-x; 
    } 
    inline void read(ll &x){ 
        bool sign=0; char ch=nc(); x=0; 
        for (;blank(ch);ch=nc()); 
        if (IOerror)return; 
        if (ch=='-')sign=1,ch=nc(); 
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; 
        if (sign)x=-x; 
    } 
    #undef OUT_SIZE 
    #undef BUF_SIZE 
}; 
using namespace fastIO;

void insert(int x,int y,int z)
{
    e[++t].y = y;e[t].n = linkk[x];e[t].v = z;linkk[x] = t;
    return;
}
void pre()
{
    rep(x,0,n-1)
    {
        int Max = 0 , Max_ = 0 , k1 = -1,k2 = -1;
        for(int j = linkk[x];j;j = e[j].n)
            if(e[j].v > Max)
            {
                Max_ = Max;k2 = k1;
                Max = e[j].v;k1 = e[j].y;
            }
            else
            if(e[j].v > Max_)
                Max_ = e[j].v,k2 = e[j].y;
        to1[x] = k1;to2[x] = k2;
        if(k2 == -1) to2[x] = to1[x];
    }
    return;
}
void make_node()
{
    memset(linkk,0,sizeof(linkk));t = 0;
    rep(i,0,n-1)
    {
        int t = to1[i];
        if(to1[t] != i) to[i] = t;
        else to[i] = t+n;

        t = to2[i];
        if(to1[t] != i) to[i+n] = t;
        else to[i+n] = t + n;
    }
    return;
}
void dfs_pre(int x,int T)
{
    if(vis[x]){
        if(x != T) tmp = -1;
        return;
    }
    vis[x] = true;
    dfs_pre(to[x],T);
    if(tmp >= 0)tmp++;
    vis[x] = false;
    return;
}
void dfs(int x,int T,int num)
{
    if(flag[x][num]) return;
    if(vis[x]) 
    {
        dis[x][num] = -1;
        flag[x][num] = true;
        return;   
    }
    if(x == T) return;
    vis[x] = true;
    dfs(to[x],T,num);
    vis[x] = false;
    dis[x][num] = dis[to[x]][num] == -1?-1:dis[to[x]][num] + 1;
    flag[x][num] = true;
    return;
}
void get()
{
    rep(i,0,2*n-1)
        if(!flag[i][1])
            dfs(i,p,1);
    rep(i,0,2*n-1)
        if(!flag[i][2])
            dfs(i,p+n,2);
    return;
}
void init()
{
    read(n);read(m);read(p);
    rep(i,1,m)
    {
        int x,y;
        read(x);read(y);
        insert(x,y,m-i+1);
        insert(y,x,m-i+1);
    }
    pre();
    dis[p][1] = dis[p+n][2] = 0;
    make_node();
    dfs_pre(p,p);len_A = tmp;tmp = 0;
    dfs_pre(p+n,p+n);len_B = tmp;
    get();
}
void work(int p,int f)
{
    int lazy = dis[to[p]][f] == -1 ? -1 : dis[to[p]][f] + 1;
    rep(i,0,n-1)
        if(dis[i][f] >= 0)
        {
            ans[0][dis[i][f]]++;
            ans[1][dis[i][f] % lazy]++;
        }
    if(lazy == -1)
        rep(i,1,Q) q[i].second += q[i].first <= 150000 ? ans[0][q[i].first] : 0;
    else
    {
        rep(i,lazy,150000) ans[0][i] += ans[0][i - lazy];
        rep(i,1,Q) q[i].second += q[i].first <= 150000 ? ans[0][q[i].first] : ans[1][q[i].first % lazy];
    }
    return;
}
int main()
{
    init();
    read(Q);
    rep(i,1,Q) read(q[i].first);
    work(p,1);
    memset(ans,0,sizeof(ans));
    work(p+n,2);
    rep(i,1,Q) printf("%d ",q[i].second);
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值