博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【CodeForces】901 C. Bipartite Segments
阅读量:5812 次
发布时间:2019-06-18

本文共 2364 字,大约阅读时间需要 7 分钟。

【题目】

【题意】给定n个点m条边的无向连通图,保证不存在偶数长度的简单环。每次询问区间[l,r]中包含多少子区间[x,y]满足只保留[x,y]之间的点和边构成的图是一个二分图。

【算法】Tarjan缩点(找环)

【题解】如果两个奇数长度的环相交,会得到一个偶数长度的简单环。所以原图是不存在偶数长度环的仙人掌(每条边只属于一个简单环)。

二分图的定义:一个图是二分图当且仅当不存在奇数长度的环。在当前仙人掌上,二分图实际上要求选择的点不存在环

也就是对于图上已有的每个环x有最小编号点min(x)和最大编号点max(x),区间不能同时包含min(x)和max(x)。(找环可以用Tarjan缩点)

为了统计区间数量,我们预处理r[i]表示以i为区间左端点,区间右端点最远到达r[i],初始r[min(x)]=max(x)-1,然后统计后缀最小值就可以得到r[]数组。

对于询问的区间i∈[l,r],若i>r则ans+=r-i+1,否则ans+=r[i]-i+1。容易发现r[]数组单调递增,所以可以二分求解转折点。

复杂度O(n log n)。

边编号不能为0 QAQ

#include
#include
#include
#include
#define ll long longusing namespace std;int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t;}const int maxn=300010;int tot=1,first[maxn],low[maxn],dfn[maxn],dfsnum=0,c[maxn],n,m,mins[maxn],maxs[maxn],s[maxn],d[maxn];ll sum[maxn],ss[maxn];bool iscut[maxn];struct edge{
int v,from;}e[maxn*2];void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}// void tarjan(int x,int fa){ low[x]=dfn[x]=++dfsnum; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ if(!dfn[e[i].v]){ tarjan(e[i].v,x); low[x]=min(low[x],low[e[i].v]); if(low[e[i].v]>dfn[x])iscut[i]=iscut[i^1]=1; }else low[x]=min(low[x],dfn[e[i].v]); }}void dfs(int x,int y){ c[x]=y;d[y]++; for(int i=first[x];i;i=e[i].from)if(!iscut[i]&&!c[e[i].v])dfs(e[i].v,y);}int main(){ n=read();m=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(); insert(u,v);insert(v,u); } tarjan(1,0);int cnt=0; for(int i=1;i<=n;i++)if(!c[i])dfs(i,++cnt); memset(mins,0x3f,sizeof(mins)); for(int i=1;i<=n;i++){ mins[c[i]]=min(mins[c[i]],i); maxs[c[i]]=max(maxs[c[i]],i); } for(int i=1;i<=n;i++)s[i]=n; for(int i=1;i<=cnt;i++)if(d[i]>1)s[mins[i]]=maxs[i]-1; for(int i=n-1;i>=1;i--)s[i]=min(s[i],s[i+1]); for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(s[i]-i+1),ss[i]=ss[i-1]+i; int q=read(); while(q--){ int u=read(),v=read(); int x=lower_bound(s+u,s+v+1,v)-s; ll ans=0; ans+=sum[x-1]-sum[u-1]; ans+=1ll*(v-x+1)*(v+1)-(ss[v]-ss[x-1]); printf("%lld\n",ans); } return 0;}
View Code

 

转载于:https://www.cnblogs.com/onioncyc/p/8133813.html

你可能感兴趣的文章
dubbo源码分析-架构
查看>>
Windows phone 8 学习笔记
查看>>
我的友情链接
查看>>
LeetCode--112--路径总和
查看>>
感悟贴2016-05-13
查看>>
DEV-C++ 调试方法简明图文教程(转)
查看>>
参加婚礼
查看>>
Java重写equals方法和hashCode方法
查看>>
Spark API编程动手实战-07-join操作深入实战
查看>>
Spring ’14 Wave Update: Installing Dynamics CRM on Tablets for Windows 8.1
查看>>
MySQL 备份与恢复
查看>>
TEST
查看>>
PAT A1037
查看>>
(六)Oracle学习笔记—— 约束
查看>>
[Oracle]如何在Oracle中设置Event
查看>>
top.location.href和localtion.href有什么不同
查看>>
02-创建hibernate工程
查看>>
Scrum之 Sprint计划会议
查看>>
svn命令在linux下的使用
查看>>
Gradle之module间依赖版本同步
查看>>