F
思维 贪心
每个手长度lil_ili,可以抓住[k−li,k+li][k-l_i,k+l_i][k−li,k+li]内的一个东西,kkk是身体所在位置,有和手等量的东西在数轴上,问能抓住所有东西的身体个数?身体必须在整点。
首先可以发现,整个数轴上的点可以分段,每段内要么都合法,要么都不合法。
因为可以这样分段:注意到触手和东西都不多n=100n=100n=100,身体位置不确定,但是东西位置是确定的,你那么可以枚举哪个手抓住了那个东西,这样身体就必须在一个区间内,这些区间的边界,把整个数轴划分成了多个部分。
那么每两个边界之间,移动一下肯定没有任何一个东西会从能拿到,变成拿不到,所以区间内的每个点要么都能拿到n个东西,要么都拿不到。
这样的分界点只有O(n2)O(n^2)O(n2)个,分成O(n2)O(n^2)O(n2)个区间,我们对于每个区间,分别检查能不能拿到所有东西即可,每次检查由于整个区间内的点都是等价的,我们随便选一个区间内的点放头即可。
检查时类似二分的check思路,我们肯定要贪心的决策,最后看能不能满足要求。这里的贪心就是,头确定了,头到每个东西距离did_idi确定了,现在可以排列序列lil_ili,要求li>=dil_i>=d_ili>=di。这是个经典贪心,都升序排序,然后看是不是满足即可,考虑交换论证,排序后的序列交换俩不会更优。
bool check(int k){
priority_queue<int>q;
rep(i,1,n){
q.push(abs(x[i]-k));
}
rep(i,1,n){
if(q.top()>l[i])return 0;
q.pop();
}
return 1;
}
void solve(void){
cin>>n;
rep(i,1,n)cin>>x[i];
rep(i,1,n)cin>>l[i];
sort(l+1,l+1+n,greater<int>());
int tot=0;
rep(i,1,n){
rep(j,1,n){
s[++tot]=x[i]-l[j]-1;
s[++tot]=x[i]+l[j];
}
}
sort(s+1,s+1+tot);
int ans=0;
rep(i,2,tot){
if(check(s[i])){
ans+=s[i]-s[i-1];
}
}
cout<<ans;
}
G
网络流 拆点
问图上是否存在一个简单路径经过a,b,c
可以考虑从中间那个点出发,就变成了找到两个不相交的简单路径b−a,b−cb-a,b-cb−a,b−c,但是不相交其实非常难办。
常规手段用不了了,考虑一下科技吧,然后我们可以发现这其实等价于从b出发,到ac两个汇点的网络流,最后看最大流是不是2即可,是2就一定能找到一个方案,因为网络流板子已经帮我们处理了一条边能用的次数了,我们只要把流量上限都设成1,就能保证每个边只能用一次,b−a,b−cb-a,b-cb−a,b−c不会用公共边了。
但是简单路径还要求公共点也不能有,这就意味着跑网络流时每个点也只能用一次,网络流模型本身可不保证这一点。
这可以拆点优化,也是个常见的优化思路,即把一个点iii拆成i,i+ni,i+ni,i+n,一个入点一个出点,所有到iii的边连到iii,所有从iii出的边连到i+ni+ni+n,然后w(i,i+n)=1w(i,i+n)=1w(i,i+n)=1,这样就能保证从iii进,i+1i+1i+1出这个事件最多发生一次,即iii点最多被用一次。
最后源点到bbb流量为2,a,ca,ca,c到汇点流量各自为1即可
int h[N * 2], e[N * 6], ne[N * 6], f[N * 6], idx, n, m, a, b, c, S, T, cur[N * 2], d[N * 2];
bool vis[N * 2];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], f[idx] = c, h[a] = idx++;
e[idx] = a, ne[idx] = h[b], f[idx] = 0, h[b] = idx++;
}
bool bfs() {
memset(d, -1, sizeof(d));
memset(vis, 0, sizeof(vis));
queue<int> q;
q.push(S);
vis[S] = 1, d[S] = 0, cur[S] = h[S];
while (q.size()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j] || !f[i]) continue;
d[j] = d[t] + 1, vis[j] = 1, cur[j] = h[j];
if (j == T) return 1;
q.push(j);
}
}
return 0;
}
int dfs(int x, int limit) {
if (x == T) return limit;
int flow = 0;
for (int i = cur[x]; (~i) && flow < limit; i = ne[i]) {
int j = e[i];
cur[x] = i;
if (d[j] != d[x] + 1 || !f[i]) continue;
int k = dfs(j, min(f[i], limit - flow));
if (!k) d[j] = -1;
f[i] -= k;
f[i ^ 1] += k;
flow += k;
}
return flow;
}
int dinic() {
int ans = 0, flow;
while (bfs()) while (flow = dfs(S, 1e9)) ans += flow;
return ans;
}
void solve(void){
memset(h,-1,sizeof h);
cin>>n>>m;
cin>>a>>b>>c;
rep(i,1,m){
int u,v;
cin>>u>>v;
add(u+n,v,1);
add(v+n,u,1);
}
S=0,T=2*n+1;
add(S,b,2);
add(a+n,T,1);
add(c+n,T,1);
rep(i,1,n){
if(i!=b)add(i,i+n,1);
else add(i,i+n,2);
}
int ans=dinic();
if(ans==2)cout<<"Yes";
else cout<<"No";
}