https://atcoder.jp/contests/arc165
D - Substring Comparison
题意
给定一个长度为 n 的序列 x,给出 m 组限制。每组限制 (ai,bi,ci,di) 表示 xai,ai+1,⋯,bi 的字典序小于 xci,ci+1,⋯,di。判断是否存在一个序列 x 满足所有的限制条件。
数据范围:2≤n≤2000,1≤m≤2000。
题解
我们依次考虑每一个限制条件,如果 ai 和 ci 相等,那么考虑 ai+1,ci+1,直到不相等或超过了 bi 或 di。如果超过了 di,那么无解。如果超过了 bi,那么该限制一定可以被满足。考虑 ai 和 ci 不等的情况,此时 ai 向 ci 连边,由此可以得到一张图。如果这张图里没有环,那么一定有解。否则,将一个强连通分量内的所有数都置成最小的数(只要是相同的数就可以,序列 x 中这些位置上的数一定都相等)。然后重复上述过程。由于每次循环至少减少一个不同的数,一共有 n 个数,所以循环次数是 O(n) 的。
代码
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
| #include <bits/stdc++.h> using namespace std; const int N=2005; int n,m,cnt,a[N],b[N],c[N],d[N],f[N],head[N],col[N],mn[N]; bool flag[N],vis[N]; vector<int> g[N],g2[N]; stack<int> s; int find(int x) {return f[x]==x?x:f[x]=find(f[x]);} void merge(int x,int y) { x=find(x),y=find(y); if(x==y) return; f[x]=y; } void dfs1(int u) { vis[u]=1; for(auto v:g[u]) if(!vis[v]) dfs1(v); s.push(u); } void dfs2(int u) { col[u]=cnt; mn[cnt]=min(mn[cnt],u); for(auto v:g2[u]) if(!col[v]) dfs2(v); } void kosaraju() { cnt=0; for(int i=1;i<=n;i++) if(!vis[i]&&find(i)==i) dfs1(i); while(!s.empty()) { int u=s.top();s.pop(); if(!col[u]) { cnt++; dfs2(u); } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n>>m; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++) cin>>a[i]>>b[i]>>c[i]>>d[i]; while(1) { for(int i=1;i<=n;i++) vis[i]=0,col[i]=0,mn[i]=0x3f3f3f3f,g[i].clear(),g2[i].clear(); for(int i=1;i<=m;i++) { if(flag[i]) continue; while(a[i]+head[i]<=b[i]&&c[i]+head[i]<=d[i]&&find(a[i]+head[i])==find(c[i]+head[i])) head[i]++; if(c[i]+head[i]>d[i]) { cout<<"No"<<'\n'; return 0; } if(a[i]+head[i]>b[i]) { flag[i]=1; continue; } g[find(a[i]+head[i])].push_back(find(c[i]+head[i])); g2[find(c[i]+head[i])].push_back(find(a[i]+head[i])); } kosaraju(); bool ok=1; for(int i=1;i<=n;i++) { if(find(i)!=i) continue; if(mn[col[i]]!=i) { merge(i,mn[col[i]]); ok=0; } } if(ok) { cout<<"Yes"<<'\n'; return 0; } } return 0; }
|