HDU4388 Simple Path 双联通+LCA

一开始用LCT查询和,果断跪了。。。。以后不修改图结构的东西还是就用rmqLCA比较好(其实还忘记了有树链剖分,但是查询l时间真的太伤了)

问一张图中从起点到终点每一个点只能走一遍,有毒少点不能走到。

先对原图求双联通分量,然后缩点成新图。注意。要建成连通分量和割点相领的图;

然后添加ROOT(有利于rmqLCA),

然后只要求出u和v的lca最后 不能到的点为n-(cntV[u]+cntV[v]-2*cntV[lca]+cnt[lca]-(dist[u]+dist[v]-2*dist[lca]))

cntV[i]:i到所在树的root的节点数(不包括最后加进去的root,下同)

dist[i]:i到root的距离

至于为什么要这样,那是因为割点重复加了

另外注意U和V相同 和 U和V不联通的情况

Simple Path

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 810    Accepted Submission(s): 147


Problem Description
Everybody knows that totalfrank has absolutely no sense of direction. Getting lost in the university or nearly supermarket is very common for him. We always worry about whether he can find his way back into our sweet base whenever he goes out alone for his class. In general, if totalfrank get lost again, we need to check his starting point and destination just in order to find out where he could be (you know this task is very common for us).
Unfortunately, poor totalfrank sometimes forgot taking his mobile phone, when this situation happens, we can’t get in touch with him. But it is so lucky that totalfrank can remember places where he had gone before in his trip from his starting point and destination at this trip so that he won’t go to such place again (he can’t remember places which he had gone during his previous trip). As we are all familiar with map, we can find out which place he couldn’t be.
However, totalfrank can always get lost, doing this same boring work makes us sleepy. So we ask totalfrank for all possible starting point and destination for him, and try to find out how many places he wouldn’t be when he chooses any pair of starting point and destination. Here comes the problem, since our university’s map is so complex (there can be many buildings which can be considered as points in our university, some pair of these point has a way while others hasn’t), we need a program to help us work out this problem.
 

Input
The input contains no more than 10 test cases. 
For each test case: 
The first line includes two integers N (1 <= N <= 100000) and M (0 <= M <= 200000). N is the total number of nodes. M is the number of edges.
The next M lines each describe an edge of this graph in the following format:
X (0 <= X < N) Y (0 <= Y < N)
It means that there is an edge from point X to point Y. Ways are bidirectional and there are no duplicates or self-loop edges.
The next line includes only one integer Q (1 <= Q <= 100000) which indicates the total number of queries.
The next Q lines are all in the following format:
A B, which means in this query totalfrank choose A as his starting point and B as his destination.
 

Output
For each test case, first you should print “Case #x:” in a line, where x stands for the case number started with 1. Then for each query output a line contains a single integer indicating the number of places which he absolutely couldn’t be during the simple path between his starting point and destination. If there is no simple path between the starting point and destination just output N.
Leave an empty line after each test case.
 

Sample Input
  
  
4 3 0 1 1 2 1 3 1 0 2 4 4 0 1 1 2 1 3 2 3 1 0 2
 

Sample Output
  
  
Case #1: 1 Case #2: 0
 

#pragma comment(linker, "/STACK:102400000,102400000")  //。。。。。。
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<utility>
#include<map>
#include<stack>
#include<vector>
#include<cmath>

using namespace std;

#define MAXN 301000
#define MAXM 5001000

struct node
{
    int to,next;
}edge[MAXM];

int head[MAXN],en;
int n,m;
int vis[MAXN],dfn[MAXN],low[MAXN];
int col,color[MAXN],cnt[MAXN];
vector<int> bcc[MAXN];
stack<pair<int,int> > s;
map<pair<int,int>,int> mii;

void add(int a,int b)
{
    edge[en].to=b;
    edge[en].next=head[a];
    head[a]=en++;
    edge[en].to=a;
    edge[en].next=head[b];
    head[b]=en++;
}


int dis;
bool iscut[MAXN];
void dfs(int u,int fat)
{
    dfn[u]=low[u]=++dis;
    vis[u]=1;
    int child=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!vis[v])
        {
            s.push(make_pair(u,v));
            dfs(v,u);
            child++;
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                iscut[u]=1;
                col++;bcc[col].clear();
                for(;;)
                {
                    pair<int ,int> e;
                    e=s.top();s.pop();
                    if(color[e.first]!=col)
                    {
                        bcc[col].push_back(e.first);
                        color[e.first]=col;
                    }
                    if(color[e.second]!=col)
                    {
                        bcc[col].push_back(e.second);
                        color[e.second]=col;
                    }
                    if(e.first==u&&e.second==v) break;
                }
            }
        }
        else if(dfn[v]<dfn[u] && v != fat) {
           s.push(make_pair(u,v));
           low[u]=min(low[u],dfn[v]);
        }  if(fat<0 && child==1) iscut[u]=0;
    }

}


int l[MAXN],r[MAXN],mm;
void tarjan()
{
    memset(vis,0,sizeof(vis));
    memset(color,0,sizeof(color));col=0;
    memset(iscut,0,sizeof(iscut));
    dis=0;
    while(!s.empty()) s.pop();
    for(int i=0;i<n;i++)  if(!vis[i]) dfs(i,-1);
    int tag=col;
    memset(cnt,0,sizeof(cnt));
    for(int i=0;i<n;i++)
    {
        if(!iscut[i]) continue;
        color[i]=++tag;
        cnt[tag]=1;
    }
    mii.clear();mm=0;
    for(int i=1;i<=col;i++)
    {
        cnt[i]=bcc[i].size();
        for(int j=0;j<bcc[i].size();j++)
        {
            int v;
            v=bcc[i][j];
            if(!iscut[v])
            {
                color[v]=i;
                continue;
            }
            if(mii.count(make_pair(i,color[v]))==0)
            {
                l[mm]=i;
                r[mm++]=color[v];
                mii[make_pair(i,color[v])]=1;
            }
        }
    }
    col=tag;
}

int head2[MAXN];
int lab[MAXN],dep[MAXN],pos[MAXN],p;
int dp[MAXN][20];  //min

void addDag(int u,int v)
{
    edge[en].to=v;
    edge[en].next=head2[u];
    head2[u]=en++;

    edge[en].to=u;
    edge[en].next=head2[v];
    head2[v]=en++;
}


void initRMQ()
{
	for(int i=1;i<=p;i++) dp[i][0]=i;
	int k=floor(log((double)p)/log(2.0));
	int a,b,z;
	for(int j=1;j<=k;j++)
	{
		for(int i=1;i+(1<<j)-1<=p;i++)
		{
			z=i+(1<<(j-1));
			a=dp[i][j-1];
			b=dp[z][j-1];
			dp[i][j]=(dep[a]<=dep[b]?a:b);
		}
	}
}

int rmqLCA(int i,int j)
{
    if(j<i) swap(i,j);
	int z=floor(log((double)(j-i+1))/log(2.0));
	int a,b;
	a=dp[i][z];
	b=dp[j-(1<<z)+1][z];
	return dep[a]<=dep[b]?lab[a]:lab[b];
}


void dfsLCA(int u,int d)
{
	vis[u]=1,dep[p]=d,lab[p]=u;
	pos[u]=p++;
	for(int i=head2[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(vis[v]) continue;
		dfsLCA(v,d+1);
		dep[p]=d,lab[p++]=u;
	}
}


void initLCA()
{
    memset(vis,0,sizeof(vis));
    p=1;
    for(int i=0;i<=col;i++)
    {
        if(vis[i]) continue;
        dfsLCA(i,0);
    }
    p--;
    initRMQ();

}

int dist[MAXN],cntV[MAXN];

void dfsDP(int u,int x,int y)
{
    vis[u]=1;
    dist[u]=x;cntV[u]=y+cnt[u];
    for(int i=head2[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]) continue;
        dfsDP(v,x+1,y+cnt[u]);
    }
}

void initDP()
{
    memset(head2,-1,sizeof(head));
    for(int i=0;i<mm;i++)
        addDag(l[i],r[i]);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=col;i++)
    {
        if(vis[i]) continue;
        dfsDP(i,0,0);
        addDag(0,i);
    }
}

int cs;

void solve()
{
    int q;
    int u,v;
    scanf("%d",&q);
    printf("Case #%d:\n",cs++);
    for(int i=0;i<q;i++)
    {
        scanf("%d%d",&u,&v);
        int lca=rmqLCA(pos[color[u]],pos[color[v]]);
        if(u==v) printf("%d\n",n-1);
        else if(lca==0) printf("%d\n",n);
        else
        {
            u=color[u],v=color[v];
            int ans=cntV[u]+cntV[v]-2*cntV[lca]+cnt[lca]-(dist[u]+dist[v]-2*dist[lca]);
            printf("%d\n",n-ans);
        }
    }
    printf("\n");
}

int main()
{
    cs=1;
    int u,v,ans;
    while(~scanf("%d%d",&n,&m))
    {
        memset(head,-1,sizeof(head));en=0;
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        tarjan();
        initDP();
        initLCA();
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值