Educational Codeforces Round 139 (Rated for Div. 2)
Educational Codeforces Round 139 (Rated for Div. 2)
数组开小,身败名裂场。
CF1766A Extremely Round
直接前缀和递推预处理一下每个 \(n\) 的答案,询问直接输出即可。
CF1766B Notepad#
询问是否能用小于 \(n\) 次操作。
注意到能够将长度为 \(k\) 的字串进行一次二操作处理,那就一定能用长度为 \(k - 1\) 的。发现只要能一次性填上超过一个字符就能小于 \(n\) 次操作。
于是用桶存一下每个长度为 \(2\) 的字串是否在之前出现过,扫一遍即可。
CF1766C Hamiltonian Wall
实际上不会走回头路,一定从左往右或者从右往左扫,但他们本质相同。
确定了起点就确定了路径,一定先看当前列有没有没刷的,没有就往右边刷,最后检查刷完没有。
最多有两种起点,都看一遍即可。
CF1766D Lucky Chains
一眼辗转相除法(或者说更相减损法)。
发现这个所谓的 chain
上的二元组差一定,于是问题就变成了求最大的 \(k\) ,使得 \(\forall i \le k \ \ gcd(x + i , y - x) = 1\) ,这就意味着我只需要对 \(y - x\) 分解质因数,检查哪一个质因子先成为最大公约数的一部分。
分解质因数用线性筛处理。
CF1766E Decomposition
呼应数组开小,赛后重交爆警告,秒调完。
\(0\) 必然会形成新的子序列,可以直接计算贡献,然后忽略掉(后面我们均未考虑)。
考虑如果没有 \(3\) ,则每个子序列会被定性为只能填 \(1\) 或者 \(2\) 。
考虑上 \(3\) 发现 \(3\) 是给第一个子序列改性的,\(3\) 后面的下一个数必然能接过来,与前面无关,这启示我们考虑由 \(3\) 划分出的问题独立性。
发现考虑过 \(3\) 后,每个子段答案最多为 \(3\) 。我们枚举子段的左端点,我们就可以尝试计算右端点小于多少时答案是 \(1,2,3\) 。
那么我们考虑我们维护的第一个子序列,假设它的第一个值为 \(1\) ,那在找到第一个 \(3\) 之前我们只能往里面塞 \(1\)。找到以后,后面的运算几乎和左端点就是这个 \(3\) 的情况一样,而会产生区别的地方就是在找到这个 \(3\) 之前我们是否生成了被定性为 \(1,2\) 的非第一个序列的序列。然后就可以计算答案。
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pii pair <int , int>
#define pll pair <LL , LL>
#define mp make_pair
#define fs first
#define sc second
#define int long long
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
x *= f;
}
template <typename T>
void write(T x , char s='\n') {
if(!x) {putchar('0');putchar(s);return;}
if(x<0) {putchar('-');x=-x;}
T t = 0 , tmp[25] = {};
while(x) tmp[t ++] = x % 10 , x /= 10;
while(t -- > 0) putchar(tmp[t] + '0');
putchar(s);
}
const int MAXN = 3e5 + 5;
int n , a[MAXN];
LL val[MAXN][4];
signed main() {
read(n);
LL ans = 0;
for (int i = 1; i <= n; ++i) {
read(a[i]);
if(!a[i]) ans += 1ll * i * (n - i + 1);
}
for (int i = n , las = n + 1; i >= 1; --i) if(a[i] == 3) {
int p = 0;
for (int j = i + 1; j < las; ++j) if(a[j]) {
p = a[j];
break;
}
for (int s = 0; s < 4; ++s) {
int c = 0;
val[i][s] = (s & 1) + ((s & 2) >> 1) + 1;
for (int j = i + 1; j < las; ++j) {
if(a[j] && a[j] != p) {
c |= a[j];
}
val[i][s] += ((s | c) & 1) + (((s | c) & 2) >> 1) + 1;
}
val[i][s] += val[las][s | c];
}
las = i;
}
for (int i = n; i >= 1; --i) {
int o = 0 , t = 0 , po = n + 1 , pt = n + 1;
int lval = 0;
for (int j = i; j >= 1; --j) {
if(a[j] == 3) {
i = j;
ans += val[i][0];
break;
}
else if(a[j] == 1) {
o ++;po = j;
}
else if(a[j] == 2) {
t ++;pt = j;
}
if(!o && !t) ans += (lval = val[i + 1][0]);
else if(po < pt) {
if(!a[j]) ans += lval;
else if(t) ans += (lval = (pt - j) + 2 * (i - pt + 1) + val[i + 1][2]);
else ans += (lval = (i - j + 1) + val[i + 1][0]);
}
else {
if(!a[j]) ans += lval;
else if(o) ans += (lval = (po - j) + 2 * (i - po + 1) + val[i + 1][1]);
else ans += (lval = (i - j + 1) + val[i + 1][0]);
}
}
if(a[i] != 3) i = 0;
}
write(ans);
return 0;
}
CF1766F MCF
很经典的费用流题目。
流量奇偶限制很容易处理。
如果要求是奇数到它,那一点流量从源点连过来(如果是它到别的点是奇数流量,那就把它到汇点连一条边),为了保证他能够被走到,我们给它赋上一个极小的权。
然后把每条边流量除以二,费用翻倍。然后把汇点到 \(1\) 连一条边。
但是我们要保证这些赋了特殊边权的边被全部跑满,并且费用最小。
如果我们把 \(n\) 到汇点的流量连多了就会出现费用跑多了的现象,于是我们可以二分这一条边的流量使得它能满足我们全部的要求。
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pii pair <int , int>
#define pll pair <LL , LL>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
x *= f;
}
template <typename T>
void write(T x , char s='\n') {
if(!x) {putchar('0');putchar(s);return;}
if(x<0) {putchar('-');x=-x;}
T t = 0 , tmp[25] = {};
while(x) tmp[t ++] = x % 10 , x /= 10;
while(t -- > 0) putchar(tmp[t] + '0');
putchar(s);
}
const int MAXN = 1e3 + 5;
int head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , edge[MAXN << 1] , w[MAXN << 1] , id[MAXN << 1] , cnt;
inline void add(int u , int v , int c , int val , int i) {
nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = c;w[cnt] = val,id[cnt] = i;
nxt[++cnt] = head[v];head[v] = cnt;to[cnt] = u;edge[cnt] = 0;w[cnt] = -val,id[cnt] = -i;
}
int cur[MAXN] , num , s , t;
LL dis[MAXN];
bool bfs() {
for (int i = 1; i <= num; ++i) dis[i] = 1e18 , cur[i] = head[i];
queue <int> q;
q.push(s);dis[s] = 0;
bool fl = 0;
while(!q.empty()) {
int x = q.front();
q.pop();
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(!edge[i] || dis[v] <= dis[x] + w[i]) continue;
dis[v] = dis[x] + w[i];
if(v == t) fl = 1;
else q.push(v);
}
}
return fl;
}
LL Ans , res[MAXN];
int vis[MAXN];
int Dinic(int x , int flow) {
if(x == t) {
Ans += dis[x] * flow;
return flow;
}
int rest = flow;
vis[x] = 1;
for (int i = cur[x]; i && rest; i = nxt[i]) {
int v = to[i];
cur[x] = i;
if(!edge[i] || dis[v] != dis[x] + w[i] || vis[v]) continue;
int k = Dinic(v , min(rest , edge[i]));
edge[i] -= k , edge[i ^ 1] += k , rest -= k;
if(!k) dis[v] = -1e18;
}
vis[x] = 0;
return flow - rest;
}
int n , m , deg[MAXN] , ty[MAXN];
LL tmp;
LL check(LL mid) {
Ans = tmp;
for (int i = 2; i <= cnt; i += 2) edge[i] += edge[i ^ 1] , edge[i ^ 1] = 0;
edge[cnt ^ 1] = mid;
int flow = 0;
while(bfs())
flow += Dinic(s , 1e9);
return Ans;
}
int main() {
cnt = 1;
read(n),read(m);
num = n + 2 , s = n + 1 , t = n + 2;
for (int i = 1; i <= m; ++i) {
int u , v , c , w;
read(u),read(v),read(c),read(w);
add(u , v , c >> 1 , w << 1 , i);
deg[u] -= (c & 1) , deg[v] += (c & 1);
Ans += (c & 1) * w;ty[i] = (c & 1);
}
add(s , 1 , 1e9 , 0 , 0);
for (int i = 2; i < n; ++i) if(deg[i]) {
if(deg[i] & 1) {
puts("Impossible");
return 0;
}
if(deg[i] > 0) add(s , i , deg[i] / 2 , -1e9 , 0) , Ans += 1e9 * deg[i] / 2;
if(deg[i] < 0) add(i , t , -deg[i] / 2 , -1e9 , 0) , Ans -= 1e9 * deg[i] / 2;
}
add(n , t , 0 , 0 , 0);
int l = 0 , r = 1e9 , val = 1e9 + 1;
tmp = Ans;
while(l <= r) {
int mid = (l + r) >> 1;
LL cur = check(mid);
if(cur < (int)1e8 && cur > (int)-1e8) val = mid , r = mid - 1;
else l = mid + 1;
}
if(val == (int)1e9 + 1) {
puts("Impossible");
return 0;
}
l = val , r = 1e9;
pll now = mp(1e18 , 0);
while(l <= r) {
int mid = (l + r) >> 1;
LL Lval = check(mid) , Rval = check(mid + 1);
if(Lval > Rval) now = min(now , mp(Rval , mid + 1ll)) , l = mid + 1;
else now = min(mp(Lval , mid * 1ll) , now) , r = mid - 1;
}
check(now.sc);
puts("Possible");
for (int x = 1; x <= n; ++x) {
for (int i = head[x]; i; i = nxt[i]) if(id[i] > 0) {
res[id[i]] = edge[i ^ 1] * 2 + ty[id[i]];
}
}
for (int i = 1; i <= m; ++i) write(res[i] , ' ');
puts("");
return 0;
}