0%

[COCI2021-2022#5] Usmjeravanje

思维题,但是我没有思维,所以我不会

看完题解之后感觉这道题其实是很可做的,推导过程也很顺畅

题目链接

题意

现在有两条河岸,分别坐落着 座城市,对于同一条河岸的两座城市 都有一条有向边

现在还有 条额外边,第 条边连接了上河岸第 座城市与下河岸第 座城市,这些额外边都还没有定向。

为每一条额外边指定一个方向,使得强连通分量个数最少

输出最少的个数,并构造方案。

题解

COCI 的题总是给我一种去了就要爆零的感觉。

本题的难点显然在于构造方案,因为方案出来了,只需要一个 tarjan 就可以求出第一问的答案。

来想一想,对于每一条边来说,怎样连会产生贡献?

对于某一条边 来说:

对于所有边 ,则两条边不会有任何影响。

对于所有边 ,则两条边不会有任何影响。

上面两条比较显然,因为无论怎么定向,都不会形成新的强连通分量。

当出现这种红边 时,边一定是 更优。

当出现这种蓝边 时,边一定是 更优。

值得一提的是,当红蓝边同时出现时,第 条边已经不重要了,因为无论方向如何,此时一定会有一个更大的绿色强连通分量。

有了这几个观察,这道题似乎就不那么难了。

先将所有边排序,第一关键字为 从小到大,第二关键字为 从大到小。

则对于每一条边 红边只可能出现在它的前面,蓝边只可能出现在它的后面

考虑按照 从小到大的顺序,指定边 的方向。

为前 条边中,从下到上连的所有边中,最大的 (若不存在这种边,则 )。

分两种情况分类讨论。

,则 有红边,则边方向为

显然,若 之后没有蓝边,此决策为最优决策;若 之后有蓝边,根据上面的讨论,一定会出现 “绿色”循环, 的两种决策等价。

,则 一定没有红边,则边方向为 ,并令

显然,若 之后有蓝边,则此决策为最优决策;否则, 既没有蓝边也没有红边, 不会对答案造成任何影响,两种决策等价。

这两种情况下,我们做的决策都是全局最优决策,所以一定可以使得强连通分量个数最少。

特别地,你会发现 (虽然我们并没有用到这个性质

方案构造完了,随便 tarjan 一下答案就出来了。

时间复杂度

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
template<const int N,const int M>
struct graph
{
int head[N+5];
int t[M+5];
int x[M+5];
int cntm;
graph(){ cntm=1;}
inline void clear(int n=N)
{
cntm=1;
for(int i=1;i<=n;i++) head[i]=0;
}
inline void ad(int u,int v)
{
// printf("ad %d %d\n",u,v);
cntm++;
t[cntm]=v;
x[cntm]=head[u];
head[u]=cntm;
}
inline void add(int u,int v)
{
ad(u,v);
ad(v,u);
}
inline int st(int num){ return head[num];}
inline int to(int num){ return t[num];}
inline int nx(int num){ return x[num];}
};
graph<400000,800000>g;
struct node
{
int x,y;
int id;
}a[200001];
int sta[400001],top;
int qans[200001];
int dfn[400001];
int low[400001];
bool in[400001];
int n,m,q;
int tot;
int tim;
void dfs(int num)
{
low[num]=dfn[num]=++tim;
sta[++top]=num,in[num]=1;
for(int i=g.st(num);i;i=g.nx(i))
{
int p=g.to(i);
if(!dfn[p])
{
dfs(p);
low[num]=min(low[num],low[p]);
}
else if(in[p]) low[num]=min(low[num],dfn[p]);
}
if(low[num]!=dfn[num]) return ;
int now;tot++;
do
{
now=sta[top--],in[now]=0;
}while(now!=num);
}
int main()
{
n=read(),m=read(),q=read();
for(int i=1;i<n;i++) g.ad(i,i+1);
for(int i=1;i<m;i++) g.ad(i+n,i+n+1);
for(int i=1;i<=q;i++)
{
a[i].x=read(),a[i].y=read();
a[i].id=i;
}
sort(a+1,a+q+1,[](node a,node b)
{
if(a.x!=b.x) return a.x<b.x;
return a.y>b.y;
});
int now=0;
memset(qans,-1,sizeof(qans));
for(int i=1;i<=q;i++)
{
if(now>=a[i].y)
{
qans[a[i].id]=0;
g.ad(a[i].x,n+a[i].y);
}
else
{
qans[a[i].id]=1;
g.ad(n+a[i].y,a[i].x);
now=a[i].y;
}
}
for(int i=1;i<=n+m;i++) if(!dfn[i]) dfs(i);
printf("%d\n",tot);
for(int i=1;i<=q;i++) printf("%d ",qans[i]);
return 0;
}

感谢观看!