(sf E - Two Houses)
解法
好笑,我甚至不知道竞赛图是啥。
结论 1:竞赛图强连通缩点后的强连通分量可以连成一条链,前面点向后面点连边。
具体证明在 第一个链接。你还会发现它们的拓扑序都是互异的。
结论 2:对于强连通分量 (s,t),若 (s) 的拓扑序小于 (t) 的拓扑序,那么对于 (forall xin s,forall yin t) 都有 ( ext{ind}_x< ext{ind}_y)。
由 结论 1
可得拓扑序在 (t) 之前的强连通分量中的点均向 (y) 连边,而 (x) 的入度取最大时也不过是拓扑序在 (s) 之前的强连通分量中的点和在 (s) 中的其它点向它连边。故结论得证。
结论 3:若 (x,y) 满足 ( ext{ind}_x< ext{ind}_y),那么 (x) 可以到达 (y)。
分类讨论。
- (x,y) 不属于同一个强连通分量。显然。
- (x,y) 属于同一个强连通分量。显然。
由 结论 3
我们可以直接得出入度小的点可以到达入度大的点,同时我们还可以询问入度大的点是否可以到达入度小的点,这样就可以判断 ((x,y)) 能否相互到达。
这样就有一个 (mathcal O(n^2)) 做法:枚举所有点对,将它们的入度差从大到小进行排序再依次询问。显然回答为 Yes
时我们也没必要继续询问下去了。
完了吗?
离谱的是,这题不仅可以 (mathcal O(n)) 做,而且根本不需要查询…
结论 4:将点按入度进行排序,则同一个强连通分量的点是连续的。
由 结论 2
易得。
结论 5:将点按入度进行排序,对于 第一个 满足 ( ext{C}(i,2)=sum_{j=1}^i ext{ind}_j) 的 (i),区间 ([1,i]) 构成一个强连通分量。
首先不管怎样有 ( ext{C}(i,2)lesum_{j=1}^i ext{ind}_j),因为在 ([1,i]) 之间就有 ( ext{C}(i,2)) 条边。那么满足条件就意味着外部没有连向 ([1,i]) 的边,所以外部的点不可能和内部的点形成强连通分量。
所以 ([1,i]) 可能形成一个或多个强连通分量。但真的可能有多个吗?假设 ([1,x],[x+1,i]) 形成两个强连通分量,那么一定有外部点 (y) 向 ([1,x]) 中某点 (z) 连边,又由于 ( ext{ind}_z< ext{ind}_y) 即 (z) 可以到达 (y),那么 (y) 可以加入 ([1,x]) 这个强连通分量,矛盾。
结论 6:将点按入度进行排序,如果到前 (i) 个点的入度和恰好为 ( ext{C}(i,2)) 则出现了一个新的强连通分量。假设上一次符合条件的点是 (lst),则 ((lst,i]) 构成了一个新的强连通分量。
由 结论 5
可知,如果我们能证明满足条件时有 ( ext{C}(i-lst,2)=sum_{j=lst+1}^i ext{ind}_j) 即可。
假设把 ([1,i]) 顺次分割成大小为 (a_1,a_2,...,a_m) 的强连通分量,剩下最后大小为 (t) 的一坨。那么那一坨的入度和可以被这样表示:
其中 (tmp) 为 (t imes sum_{j=1}^m a_j+a_m imes sum_{j=1}^{m-1} a_j+a_{m-1} imes sum_{j=1}^{m-2} a_jcdots)(博主的表达能力实在有限)。
稍微拆拆就容易证明结果是 (frac{t imes(t-1)}{2})。不想写了。
用 结论 6
实现即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
#define debug(...) do {cerr<<__LINE__<<" : ("#__VA_ARGS__<<") = "; Out(__VA_ARGS__); cerr<<flush;} while(0)
template <typename T> void Out(T x) {cerr<<x<<"
";}
template <typename T,typename ...I> void Out(T x,I ...NEXT) {cerr<<x<<", "; Out(NEXT...);}
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
typedef pair <int,int> pii;
const int maxn=505;
int n;
pii s[maxn];
vector <int> buc[maxn];
int main() {
int cnt=0,lst=0,sum=0,ans=-1,l=0,r=0;
n=read(9);
rep(i,1,n) buc[read(9)].push_back(i);
rep(i,0,n) for(int j=0;j<buc[i].size();++j) s[++cnt]=make_pair(i,j);
rep(i,1,cnt) {
sum+=s[i].first;
if(sum==i*(i-1)/2) {
if(lst^(i-1)) {
if(ans<s[i].first-s[lst+1].first) {
ans=s[i].first-s[lst+1].first;
l=buc[s[i].first][s[i].second],r=buc[s[lst+1].first][s[lst+1].second];
}
}
lst=i;
}
}
printf("! %d %d
",l,r); fflush(stdout);
return 0;
}