Codeforces 160
160 D
题意
#define MST 最小生成树
给你一张图
众所周知,一张图的MST不止一个
判断每条边是属于所有的MST,还是属于一部分MST,还是不可能属于任何一个MST
( \(2\le n\le 10^5,n-1\le m\le \min (10^5,\frac{n(n-1)}{2})\) )
Examples
Input
4 5
1 2 101
1 3 100
2 3 2
2 4 2
3 4 1
Output
none
any
at least one
at least one
any
Input
3 3
1 2 1
2 3 1
1 3 2
Output
any
any
none
Input
3 3
1 2 1
2 3 1
1 3 1
Output
at least one
at least one
at least one
解
使用kruskal算法,先将边排序,然后将边权相同的边放在一起处理
对于一条边,如果边的两个端点在并查集中已经被合并,那么立即放弃,none
将所有没被放弃的边建一张新图,然后跑一遍Tarjan求割边,在所有这些边中,如果一条边是割边,则any,否则at least one
注意,这张图中的点是上一次操作求出的联通块的缩点
然后进行并查集的合并操作
如何求割边(桥)? https://www.cnblogs.com/AireenYe/p/6049061.html
Code
#include<bits/stdc++.h>
#define maxn 200003
#define INF 1050000000
using namespace std;
int n,m,flag[maxn];
namespace MST{
struct edge{
int to,next,num;
}e[maxn];
int head[maxn],cnte;
void add(int u,int v,int num){
e[++cnte].to=v;
e[cnte].num=num;
e[cnte].next=head[u];
head[u]=cnte;
}
int low[maxn],dfn[maxn],cntdfn;
void tarjan(int u,int last){
low[u]=dfn[u]=++cntdfn;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(e[i].num==last)continue;
if(!dfn[v]){
tarjan(v,e[i].num);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])flag[e[i].num]=2;
}
else{
low[u]=min(low[u],dfn[v]);
}
}
}
}
struct edge{
int from,to,w,num;
bool operator <(const edge& x)const{
return w<x.w;
}
}e[maxn];
int cnte;
void add(int u,int v,int w,int num){
e[++cnte].from=u;
e[cnte].to=v;
e[cnte].w=w;
e[cnte].num=num;
}
int fa[maxn];
void init(int n){
for(int i=1;i<=n;i++)fa[i]=i;
}
int find(int x){
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
void kruskal(){
sort(e+1,e+cnte+1);
init(n);
int i,j;
for(i=1;i<=cnte;i=j+1){
for(j=i;e[j].w==e[i].w&&j<=cnte;j++);j--;
for(int k=i;k<=j;k++){
int u=find(e[k].from),v=find(e[k].to);
if(u!=v){
MST::head[u]=MST::head[v]=-1,MST::low[u]=MST::low[v]=MST::dfn[u]=MST::dfn[v]=0;
}
}
MST::cnte=-1,MST::cntdfn=0;
for(int k=i;k<=j;k++){
int u=find(e[k].from),v=find(e[k].to);
if(u!=v){
MST::add(u,v,e[k].num),MST::add(v,u,e[k].num);
flag[e[k].num]=1;
}
}
for(int k=i;k<=j;k++){
int u=find(e[k].from),v=find(e[k].to);
if(u!=v){
if(!MST::dfn[u])MST::tarjan(u,-1);
}
}
for(int k=i;k<=j;k++){
int u=find(e[k].from),v=find(e[k].to);
if(u!=v){
fa[u]=v;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w,i);
}
kruskal();
for(int i=1;i<=m;i++){
printf("%s\n",flag[i]==0?"none":(flag[i]==1?"at least one":"any"));
}
return 0;
}
160 E
题意
\(n\) 辆公交车, \(m\) 个人
公交车有三个属性:\(s,f,t\) (在 \(t\) 时刻从 \(s\) 到 \(t\) )
人也有三个属性:\(l,r,b\) (在 \(b\) 时刻从 \(l\) 到 \(r\) )
一个人能上一辆公交车的条件:\(s\le l,r\le f,b\le t\)
现在,每个人要从 \(l\) 赶到 \(r\) ,只能坐一辆公交车,而且要搭乘最早的一辆
求出每个人将会搭乘哪一辆公交车,如果没有符合条件的公交车,输出 \(-1\)
没有两辆公交车的 \(t\) 相同
( n,m\le 10^5,s,f,t,l,r,b\le 10^9 )
Examples
Input
4 3
1 10 10
5 6 2
6 7 3
5 7 4
5 7 1
1 2 1
1 10 11
Output
4 1 -1
Input
1 1
1 1000000000 1000000000
1 1000000000 1000000000
Output
1
解
先把 \(t,b\) 离散化
把公交车和人放在同一个序列中
然后以左端点为第一条件,右端点为第二条件对该序列排序
以时间为区间建一棵线段树,单点修改
叶节点存公交车的右端点、编号
非叶节点存该区间内最右边的右端点
pushup:当前节点的右端点为两个子节点中右端点的max
insert:修改节点 hash(t) 的右端点、编号
query:如果左儿子区间有答案就返回左儿子的答案,否则返回右儿子的答案
Code
#include<bits/stdc++.h>
#define maxn 400003
#define INF 1050000000
#define ha(x) (lower_bound(mp+1,mp+cntmp+1,(x))-mp)
using namespace std;
struct T{
int l,r,ti,num;
bool operator <(const T& x)const{
return l==x.l?num<x.num:l<x.l;
}
}a[maxn];
int n,m,cntmp,mp[maxn],ans[maxn];
struct node{
int rig,num;
node():rig(-1),num(-1){}
}t[maxn<<2];
void pushup(int p){
t[p].rig=max(t[p<<1].rig,t[p<<1|1].rig);
}
void change(int p,int l,int r,int pos,int k,int num){
if(l==r){
t[p].rig=k;
t[p].num=num;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)change(p<<1,l,mid,pos,k,num);
else change(p<<1|1,mid+1,r,pos,k,num);
pushup(p);
}
int query(int p,int l,int r,int seg_l,int seg_r,int k){
if(t[p].rig<k)return -1;
if(l==r)return t[p].num;
int mid=(l+r)>>1;
if(seg_l<=mid){
int tmp=query(p<<1,l,mid,seg_l,seg_r,k);
if(tmp!=-1)return tmp;
}
if(seg_r>mid){
return query(p<<1|1,mid+1,r,seg_l,seg_r,k);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n+m;i++){
scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].ti);
a[i].num=i;
mp[++cntmp]=a[i].ti;
}
sort(mp+1,mp+cntmp+1);
cntmp=unique(mp+1,mp+cntmp+1)-mp-1;
sort(a+1,a+n+m+1);
for(int i=1;i<=n+m;i++){
if(a[i].num>n){
ans[a[i].num-n]=query(1,1,cntmp,ha(a[i].ti),cntmp,a[i].r);
}
else{
change(1,1,cntmp,ha(a[i].ti),a[i].r,a[i].num);
}
}
for(int i=1;i<=m;i++)printf("%d ",ans[i]);
return 0;
}