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;
}
posted @ 2022-12-15 23:08  Reanap  阅读(96)  评论(0编辑  收藏  举报