Codeforces Round #453 (Div. 1) 901C C. Bipartite Segments

  http://codeforces.com/contest/901/problem/C

  codeforces 901C

  首先因为图中没有偶数长度的环,所以:

    1.图中的环长度全是奇数,也就是说,只要这个子图中存在环,那么这个子图肯定是不合法的。

    2.图中任意两个环没有公共边,否则合成的环的长度必然是偶数

  所以用一个dfs可以找到所有环

  然后对于每个环,求出这个环里面的最大值 li 和最小值 ri 。那么显然对于区间 [1,li] 中的任意位置 x,这个数的右区间的取值范围必然小于 ri

  所以可以通过一个线段树找出所有左节点对应的右节点最大值,这里对于节点 x ,记其对应的右节点最大值为 rib[x]

  对于q个询问

  设给定询问的左端点为 ql,右端点为 qr

  那么二分找到 [ql,qr]区间中的第一个数,她的右端点最大值大于等于 qr,记这个位置为plc

  那么 [ql,qr] 这个区间可以被分为 [ql,plc-1] [plc,qr],分别计算答案,

 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;

const int INF=1e9+44;
const int M=3e5+44;

struct node{
    int u,v;
    int next;
}edge[2*M];

queue<int> que;
int head[M],num;
int dg[M];

void addedge(int u,int v)
{
    edge[num].u=u;
    edge[num].v=v;
    edge[num].next=head[u];
    head[u]=num++;
}

void init()
{
	num=0;
   	memset (head,-1,sizeof(head));
}
   		
int n,m;
int tree[M*3],tag[M*3];

void build(int rt,int li,int ri)
{
	tag[rt]=INF;
	if(li==ri)
	{
		tree[rt]=n;
		return ;
	}
	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
	build(lc,li,mid);
	build(rc,mid+1,ri);
	tree[rt]=min(tree[lc],tree[rc]);
}

void pushdown(int rt,int li,int ri)
{
	if(li==ri)
	{
		tag[rt]=INF;
		return ;
	}
	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
	tag[lc]=min(tag[lc],tag[rt]);
	tag[rc]=min(tag[rc],tag[rt]);
	tag[rt]=INF;
	tree[lc]=min(tree[lc],tag[lc]);
	tree[rc]=min(tree[rc],tag[rc]);
}

void update(int rt,int li,int ri,int lq,int rq,int val)	//change to val
{
	if(lq<=li && ri<=rq)
	{
		tag[rt]=min(tag[rt],val);
		tree[rt]=min(tree[rt],val);
		return ;
	}
	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
	if(tag[rt]!=INF)
		pushdown(rt,li,ri);
	if(mid>=lq)
		update(lc,li,mid,lq,rq,val);
	if(mid+1<=rq)
		update(rc,mid+1,ri,lq,rq,val);
	tree[rt]=min(tree[lc],tree[rc]);
}

int query(int rt,int li,int ri,int lq,int rq)	//get min val
{
	int ret=INF;
	if(lq<=li && ri<=rq)
		return tree[rt];
	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
	if(tag[rt]!=INF)
		pushdown(rt,li,ri);
	if(mid>=lq)
		ret=min(ret,query(lc,li,mid,lq,rq));
	if(mid+1<=rq)
		ret=min(ret,query(rc,mid+1,ri,lq,rq));
	return ret;
}

int stk[M],lstk,dlt[M],vis[M];

int dfs(int rt,int pa)
{
//	cout<<"rt:"<<rt<<endl;
	int v;
	vis[rt]=1,stk[++lstk]=rt;
	for(int i=head[rt];i!=-1;i=edge[i].next)
	{
		v=edge[i].v; 
		if(v==pa || dlt[v]) continue;
		if(vis[v])
		{
			int li=INF,ri=0;
			while(lstk>0)
			{
//				cout<<stk[lstk]<<" . ";
				dlt[stk[lstk]]=1;
				li=min(li,stk[lstk]),ri=max(ri,stk[lstk]);
				lstk--;
				if(stk[lstk+1]==v)
					break;
			}
//			cout<<endl;
//			cout<<li<<' '<<ri<<endl;
			update(1,1,n,1,li,ri-1);
		}
		else
			dfs(v,rt);
	}
	if(dlt[rt]==0)
		lstk--;
}

void makeTree()
{
	build(1,1,n);
	memset(vis,0,sizeof(vis));
	memset(dlt,0,sizeof(dlt));
	for(int i=1;i<=n;i++)
		if(vis[i]==0)
			dfs(i,-1);
}

int q;
int rib[M];
ll pre[M];

void solve(int qli,int qri)
{
	ll ans;
	int i,j;
	ll cnt1,cnt2;
	int li=qli-1,ri=qri,mid;
	while(li<ri-1)
	{
		mid=(li+ri)>>1;
		if(rib[mid]>=qri)
			ri=mid;
		else li=mid;
	}
	cnt1=li-qli+1; cnt2=qri-li;
	ans=pre[li]-pre[qli-1]-(2*qli-3+cnt1)*cnt1/2+(1+cnt2)*cnt2/2;
	printf("%I64d\n",ans);	
}

int main()
{
	int a,b;
	init();
	scanf("%d%d",&n,&m);
	memset(dg,0,sizeof(dg));
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		addedge(a,b);
		addedge(b,a);
		dg[a]++,dg[b]++;
	}
	makeTree();
	for(int i=1;i<=n;i++)
		rib[i]=query(1,1,n,i,i);
	pre[0]=0;
	for(int i=1;i<=n;i++)
		pre[i]=pre[i-1]+rib[i];
//	for(int i=1;i<=n;i++)
//		cout<<rib[i]<<' ';
//	cout<<endl;
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&a,&b);
		solve(a,b);
	}
	return 0;
}

  

  

 

posted @ 2017-12-20 17:28  太阳星人FxxL  阅读(454)  评论(0编辑  收藏  举报