您的位置:首页 > 运维架构

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).

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: