CF 652 C(思维好题) D(排序+BIT维护) E(边双联通分量+缩点).
2017-09-23 10:22
344 查看
题意:给出[1.n]排列的序列a,m对数(xi,yi) 定义区间合法为:区间不能包含着m对数中的任意一对,
即x[i],y[i]不能同时出现在一个区间,问有多少个区间为合法的? n,m<=3e5,x[i]!=y[i].
首先注意到若[l,r]为合法区间 则[l+1,r]肯定也为合法区间.尝试用two pointer解决 发现正着做好麻烦 移动l时还要删除操作.
[l,r]为合法,[l,r+1]为非法 则l-1开头的左端点 最多到r.此时右端点取决于min(r,f[i])..倒着做.
只要记录i为左端点时,后面和a[i]碰到的第一个非法位置a[j], 若[l,r]为合法,则k属于[l,r]->f[a[k]]>r
则从后往前枚举左端点 维护最远能到的rg(是非递增的)即可.O(n+m).
将左端点l从大到小排序,则被第i个线段包含的线段j只可能在i之前 l[j]>l[i] 只要求出有多少个r[j]<r[i]的即可.
离散化后树状数组记录R[j] 求一次r[i]前缀和即可 O(NlogN).
题意:n点m边联通图,边的权值为0或者1.问从a开始能否经过至少一条权值为1的边然后到达b,每条边只能走一次(中间可以经过b)
n,m<=3e5.
利用边双联通分类性质.
边双联通分量.
性质1:无桥(删除任意一条边,不改变其联通性).
性质2:任意两点有至少有两条不相交路径(路径上的边都不同.反证:若a-b两个路径有相同边存在,删掉这个相同边 则a-b不联通.)
把原图中的边双联通分量进行缩点,一个边双联通分量->新点 则该点点权为1当且仅当边双联通分量中有权为1的边存在.
现在原图变为一颗树 a-b只有一条路径存在.
看a-b路径上是否有点/边权值为
4000
1的存在.在树上跑最长路(每点经过一次)即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=6e5+20,inf=0x3f3f3f3f;
struct Graph{
int h
,p
,fr
,v
,w
,val
,dis
,vis
,cnt,id,n;
int dfn
,low
,instack
,stack
,belong
,bcc,top;
Graph():cnt(0){}
void add(int a,int b,int c)
{
p[++cnt]=h[a],fr[cnt]=a,v[cnt]=b,w[cnt]=c,h[a]=cnt;
p[++cnt]=h[b],fr[cnt]=b,v[cnt]=a,w[cnt]=c,h[b]=cnt;
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++id;
instack[u]=1,stack[++top]=u;
for(int i=h[u];i;i=p[i])
{
if(v[i]==fa) continue;
if(!dfn[v[i]])
{
tarjan(v[i],u);
low[u]=min(low[v[i]],low[u]);
}
else if(instack[v[i]])
low[u]=min(low[u],dfn[v[i]]);
}
int i;
if(dfn[u]==low[u])
{
++bcc;
do{
i=stack[top--];
belong[i]=bcc;
instack[i]=0;
}while(i!=u);
}
}
void do_bcc()
{
id=0,top=0,bcc=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i,0);
}
void rebuild(Graph &out)//
{
for(int i=1;i<=cnt;i+=2)
{
if(belong[fr[i]]!=belong[v[i]])
out.add(belong[fr[i]],belong[v[i]],w[i]);
else if(w[i])
out.val[belong[fr[i]]]=1;
}
}
bool calc(int x,int y)
{
queue<int> q;
q.push(x);
dis[x]=val[x],vis[x]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=h[u];i;i=p[i])
{
if(!vis[v[i]])
{
if(dis[v[i]]<dis[u]+w[i]+val[v[i]])
dis[v[i]]=dis[u]+w[i]+val[v[i]];
vis[v[i]]=1,q.push(v[i]);
}
}
}
return dis[y];
}
}base,ne;
int main()
{
int n,m,i,a,b,c;
scanf("%d%d",&n,&m);
base.n=n;
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
base.add(a,b,c);
}
scanf("%d%d",&a,&b);
base.do_bcc();base.rebuild(ne);
puts((ne.calc(base.belong[a],base.belong[b]))?"YES":"NO");
return 0;
}
即x[i],y[i]不能同时出现在一个区间,问有多少个区间为合法的? n,m<=3e5,x[i]!=y[i].
首先注意到若[l,r]为合法区间 则[l+1,r]肯定也为合法区间.尝试用two pointer解决 发现正着做好麻烦 移动l时还要删除操作.
[l,r]为合法,[l,r+1]为非法 则l-1开头的左端点 最多到r.此时右端点取决于min(r,f[i])..倒着做.
只要记录i为左端点时,后面和a[i]碰到的第一个非法位置a[j], 若[l,r]为合法,则k属于[l,r]->f[a[k]]>r
则从后往前枚举左端点 维护最远能到的rg(是非递增的)即可.O(n+m).
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=5e5+5,inf=0x3f3f3f3f; int n,m,a ,pos ; vector<int> f ; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]),pos[a[i]]=i; for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); int px=pos[x],py=pos[y]; if(px>py) swap(px,py); f[px].push_back(py); } int rg=n+1; ll ans=0; for(int l=n;l>=1;l--) { for(int j=0;j<f[l].size();j++) rg=min(rg,f[l][j]); ans+=rg-l; } cout<<ans<<endl; return 0; }题意:n个线段[li,ri] 端点不会重合,问第i条线段包含多少条其他的线段[i=1,2..n]. n<=2e5,-1e9<=l[i],r[i]<=1e9
将左端点l从大到小排序,则被第i个线段包含的线段j只可能在i之前 l[j]>l[i] 只要求出有多少个r[j]<r[i]的即可.
离散化后树状数组记录R[j] 求一次r[i]前缀和即可 O(NlogN).
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=4e5+5,inf=0x3f3f3f3f; int n,c ; struct node{ int l,r,id; }a ; bool cmp(node a,node b) { return a.l>b.l; } vector<int> v; int getid(int x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1; } int res ; int lowbit(int x) { return x&-x; } void update(int x,int val) { for(int i=x;i<N;i+=lowbit(i)) c[i]+=val; } int query(int x) { int res=0; for(int i=x;i>0;i-=lowbit(i)) res+=c[i]; return res; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].r),v.push_back(a[i].l),v.push_back(a[i].r),a[i].id=i; sort(a+1,a+1+n,cmp); sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); for(int i=1;i<=n;i++) { int id=getid(a[i].r); int sum=query(id); res[a[i].id]=sum; update(id,1); } for(int i=1;i<=n;i++) printf("%d\n",res[i]); return 0; }
题意:n点m边联通图,边的权值为0或者1.问从a开始能否经过至少一条权值为1的边然后到达b,每条边只能走一次(中间可以经过b)
n,m<=3e5.
利用边双联通分类性质.
边双联通分量.
性质1:无桥(删除任意一条边,不改变其联通性).
性质2:任意两点有至少有两条不相交路径(路径上的边都不同.反证:若a-b两个路径有相同边存在,删掉这个相同边 则a-b不联通.)
把原图中的边双联通分量进行缩点,一个边双联通分量->新点 则该点点权为1当且仅当边双联通分量中有权为1的边存在.
现在原图变为一颗树 a-b只有一条路径存在.
看a-b路径上是否有点/边权值为
4000
1的存在.在树上跑最长路(每点经过一次)即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=6e5+20,inf=0x3f3f3f3f;
struct Graph{
int h
,p
,fr
,v
,w
,val
,dis
,vis
,cnt,id,n;
int dfn
,low
,instack
,stack
,belong
,bcc,top;
Graph():cnt(0){}
void add(int a,int b,int c)
{
p[++cnt]=h[a],fr[cnt]=a,v[cnt]=b,w[cnt]=c,h[a]=cnt;
p[++cnt]=h[b],fr[cnt]=b,v[cnt]=a,w[cnt]=c,h[b]=cnt;
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++id;
instack[u]=1,stack[++top]=u;
for(int i=h[u];i;i=p[i])
{
if(v[i]==fa) continue;
if(!dfn[v[i]])
{
tarjan(v[i],u);
low[u]=min(low[v[i]],low[u]);
}
else if(instack[v[i]])
low[u]=min(low[u],dfn[v[i]]);
}
int i;
if(dfn[u]==low[u])
{
++bcc;
do{
i=stack[top--];
belong[i]=bcc;
instack[i]=0;
}while(i!=u);
}
}
void do_bcc()
{
id=0,top=0,bcc=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i,0);
}
void rebuild(Graph &out)//
{
for(int i=1;i<=cnt;i+=2)
{
if(belong[fr[i]]!=belong[v[i]])
out.add(belong[fr[i]],belong[v[i]],w[i]);
else if(w[i])
out.val[belong[fr[i]]]=1;
}
}
bool calc(int x,int y)
{
queue<int> q;
q.push(x);
dis[x]=val[x],vis[x]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=h[u];i;i=p[i])
{
if(!vis[v[i]])
{
if(dis[v[i]]<dis[u]+w[i]+val[v[i]])
dis[v[i]]=dis[u]+w[i]+val[v[i]];
vis[v[i]]=1,q.push(v[i]);
}
}
}
return dis[y];
}
}base,ne;
int main()
{
int n,m,i,a,b,c;
scanf("%d%d",&n,&m);
base.n=n;
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
base.add(a,b,c);
}
scanf("%d%d",&a,&b);
base.do_bcc();base.rebuild(ne);
puts((ne.calc(base.belong[a],base.belong[b]))?"YES":"NO");
return 0;
}
相关文章推荐
- poj3177—Redundant Paths(缩点+边双联通分量)
- 【强联通分量缩点】【搜索】bzoj2208 [Jsoi2010]连通数
- HDOJ 3861 - The King’s Problem tarjan求强联通分量&缩点&有向图最小路径覆盖(匈牙利)
- HDOJ 1827 - Summer Holiday 简单的tarjan求强联通分量+缩点
- 【最小割】【Dinic】【强联通分量缩点】bzoj1797 [Ahoi2009]Mincut 最小割
- BZOJ 2438 杀人游戏 强联通分量tarjan缩点
- 强联通分量 缩点 tarjan 入门题小集
- HDU4612 Warm up(强联通分量+缩点重构图后求树的直径)
- 【POJ1236】Network of Schools 强联通分量缩点(本文kosaraju)
- 【强联通分量缩点】【最短路】【spfa】bzoj1179 [Apio2009]Atm
- 【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点
- 【强联通分量缩点】【Tarjan】bzoj1051 [HAOI2006]受欢迎的牛
- sdut 2604 Thrall’s Dream (强联通分量+缩点)
- poj 3694 Network (强联通分量缩点+割桥+lca查询)
- [HAOI2006 受欢迎的牛] 强联通分量 缩点
- 强联通分量 缩点 tarjan算法
- 文章标题 POJ 1236 : Network of Schools (强联通分量+缩点)
- HDU5409---CRB and Graph 2015多校 双联通分量缩点
- HDU --- 6165 FFF at Valentine 多校第九场 【强联通缩点 + 维护拓扑序】
- poj2186 Popular Cows (强联通分量+缩点)