ccpc2019 哈尔滨 8/12

A

裸的差分约束,注意用前缀和作为点的时候给前缀和一点限制,以及就是,这题卡常x
不过赛后补题我的一发过了x,队友的比赛时T飞了…

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
 
using namespace std;
 
// a -> b (c)表示b - a <= c
 
int const N = 3005;
 
struct edge {
	int w, y, next;
	int is;
} e[N * 10];
int dis[N], last[N], ne;
queue<int> q;
int cnt[N];
bool inq[N];
int n, m1, m2;
 
inline void addedge(int x, int y, int w, int is) {
	//cerr << ">> " << x << ' ' << y << ' ' << w << ' ' << is << '\n';
	e[++ne] = { w, y, last[x], is };
	last[x] = ne;
}
 
inline void add_edge_w(int delta) {
	for (int i = 1; i <= ne; ++i) {
		if (e[i].is == 1)
			e[i].w += delta;
		if (e[i].is == 2)
			e[i].w -= delta;
	}
}
 
bool chk(int delta) {
	add_edge_w(delta);
	fill(dis, dis + n + 1, 0);
	fill(inq, inq + n + 1, 1);
	fill(cnt, cnt + n + 1, 0);
	while (!q.empty())
		q.pop();
	for (int i = 0; i <= n; ++i) 
		q.push(i);
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		inq[now] = 0;
		for (int i = last[now]; i; i = e[i].next) 
			if (dis[e[i].y] > dis[now] + e[i].w) {
				dis[e[i].y] = dis[now] + e[i].w;
				cnt[e[i].y] = cnt[now] + 1;
				if (cnt[e[i].y] > n + 1) {
					add_edge_w(-delta);
					return 0;
				}
				if (inq[e[i].y] == 0) {
					inq[e[i].y] = 1;
					q.push(e[i].y);
				}
			}
	}
	add_edge_w(-delta);
	return 1;
}
 
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d", &n, &m1, &m2);
		fill(last, last + n + 1, 0);
		ne = 0;
 
		for (int i = 1; i <= m1; ++i) {
			//s[r] - s[l - 1] >= K
			//s[l - 1] - s[r] <= -K
			int l, r, K;
			scanf("%d%d%d", &l, &r, &K);
			addedge(r, l - 1, -K, 0);
		}
 
		for (int i = 1; i <= m2; ++i) {
			//s[r] - s[l - 1] <= s[n] - K
			int l, r, K;
			scanf("%d%d%d", &l, &r, &K);
			addedge(l - 1, r, -K, 1);
		}
		for (int i = 1; i <= n; ++i) {
			//s[i] <= s[i - 1] + 1
			//s[i] >= s[i - 1]
			//s[i] - s[i - 1] <= 1
			//s[i - 1] - s[i] <= 0
			addedge(i - 1, i, 1, 0);
			addedge(i, i - 1, 0, 0);
		}
		//s[n] - s[0] >= sn
		//s[n] - s[0] <= sn
		//s[0] - s[n] <= -sn
		addedge(0, n, 0, 1);
		addedge(n, 0, 0, 2);
		
		int l = 0, r = n, ans = -1;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (chk(mid)) {
				ans = mid;
				r = mid - 1;
			} else
				l = mid + 1;
		}
		printf("%d\n", ans);
	}
}

B

发现只需要限制端点就可以限制一个选择的区间。
也就是说,题目中的条件可以转化为,如果选择了 A 1 , A 2 , . . . , A n A_1, A_2,...,A_n A1,A2,...,An
那么 ∀ i ∈ [ 1 , n ] \forall i \in [1, n] i[1,n]满足
F m − 1 ( A i − 1 , R i − 1 ) ≥ F m − 1 ( A i , R i − 1 ) F_{m - 1}(A_{i - 1}, R_{i - 1}) \geq F_{m - 1}(A_i,R_{i - 1}) Fm1(Ai1,Ri1)Fm1(Ai,Ri1)

F m − 1 ( A i − 1 , L i ) ≤ F m − 1 ( A i , L i ) F_{m - 1}(A_{i - 1}, L_i) \leq F_{m - 1}(A_i,L_i) Fm1(Ai1,Li)Fm1(Ai,Li)
用这两个条件就可以转化出一个区间,考虑DP,每次用这个区间来转移即可

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cassert>
#include <bitset>

using namespace std;

int const MOD = 1e8 + 7;
int const N = 17;

int col[1 << N];
int f[1 << N];
int g[1 << N];

int F_m(int x, int y, int m) {
	int ret = 0;
	for (int i = 0; i < m; ++i) {
		if ((x & 1) == (y & 1))
			++ret;
		else
			ret = 0;
		x >>= 1;
		y >>= 1;
	}
	return ret;
}

pair<int, int> b[1 << N];

/*
int c[105];
void dfs(int x, int n, int& ans, int m) {
	if (x > n) {
		int mul = 1;
		for (int i = 1; i <= n; ++i) {
			mul *= c[i];
			for (int k = b[i].first; k <= b[i].second; ++k) {
				for (int j = 1; j <= n; ++j)
					if (F_m(c[i], k, m) < F_m(c[j], k, m)) {
						cerr << ">> i j k Fi Fj : " << i << ' ' << j << ' ' << k << ' ' << F_m(c[i], k, m) << ' ' <<  F_m(c[j], k, m) << '\n';
						cerr << "? ";
						for (int p = 1; p <= n; ++p)
							cerr << c[p] << ' ' ;
						cerr << '\n';
						return;
					}
			}
		}
		ans += mul;
		return;
	}
	for (int i = b[x].first; i <= b[x].second; ++i) {
		c[x] = i;
		dfs(x + 1, n, ans, m);
		c[x] = 0;
	}
}
*/

int mkd_beg(int x, int y, int m) {
	int ret = 0;
	for (int i = m - 1; i >= 0; --i)
		if ((x & (1 << i)) == (y & (1 << i)))
			ret |= x & (1 << i);
		else
			break;
	return ret;
}

int mkd_lim(int x, int y, int m) {
	int ret = 0;
	for (int i = m - 1; i >= 0; --i)
		if ((x & (1 << i)) == (y & (1 << i))) {
			if (x & (1 << i))
				ret |= 1 << i;
		} else 
			break;
	--ret;
	return ret;
}

int get_sum(int l, int r) {
	if (l > r)
		return 0;
	if (l == 0)
		return g[r];
	return (MOD + g[r] - g[l - 1]) % MOD;
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int m, n;
		scanf("%d%d", &m, &n);
		for (int i = 1; i <= n; ++i) {
			int l, r;
			scanf("%d%d", &l, &r);
			b[i] = { l, r };
			for (int j = l; j <= r; ++j)
				col[j] = i;
		}
		for (int i = 0; i < (1 << m); ++i) {
			if (col[i] == 1) {
				f[i] = i;
				g[i] = i ? g[i - 1] + f[i] : f[i];
				g[i] %= MOD;
				continue;
			}
			int lim = mkd_lim(b[col[i]].first, i, m);
			int beg = mkd_beg(b[col[i] - 1].second, i, m);
			//cerr << "i : " << i << ' ' << beg << ' ' << lim << '\n';
			beg = max(beg, b[col[i] - 1].first);
			lim = min(lim, b[col[i] - 1].second);
			f[i] = 1ll * i * get_sum(beg, lim) % MOD;
			g[i] = (g[i - 1] + f[i]) % MOD;
		}
		//int chk_ans = 0;
		//dfs(1, n, chk_ans, m);
		//cerr << "C: " << chk_ans << '\n';
		printf("%d\n", get_sum(b[n].first, b[n].second));
	}
}

C

D

E

一个很傻逼的题,硬是被我写锅了x
sort 1e6怎么T嘛
不过确实存在线性做法,可以参考bzoj的mean那题

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <cassert>
 
using namespace std;
 
int const N = 1050005;
 
int son[N][2];
vector<int> a[N];
long long num[N];
int n;
pair<int, long long> det[N];
long long tot[N];
 
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		int m = 0;
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &son[i][0]);
			if (son[i][0] == 1) {
				son[i][1] = 0;
				int t;
				scanf("%d", &t);
				a[i].resize(t);
				for (auto& v : a[i]) 
					scanf("%d", &v);
			} else
				scanf("%d%d", &son[i][0], &son[i][1]);
		}
		for (int i = 1; i <= n; ++i)
			num[i] = 0;
		num[n] = 1;
		for (int i = n; i >= 1; --i)
			if (!(son[i][0] == 1 && son[i][1] == 0)) {
				num[son[i][0]] += num[i];
				num[son[i][1]] += num[i];
			}
		long long sum = 0;
		for (int i = 1; i <= n; ++i)
			if (son[i][0] == 1 && son[i][1] == 0) {
				for (auto v : a[i]) 
					det[++m] = { v, num[i] };
				sum += (long long) (a[i].size()) * num[i];
			}
		sort(det + 1, det + m + 1);
		long long mx = 0;
		long long t = 0;
		for (int i = 1; i <= m; ++i) {
			t += det[i].second;
			if (i == n || det[i].first != det[i + 1].first) {
				mx = max(mx, t);
				t = 0;
			}
		}
		//assert(mx <= sum && chk_sum == sum);
		printf("%lld\n", min((sum - mx) * 2, sum));
		for (int i = 1; i <= n; ++i) 
			// a[i].swap(std::vector<int>());
			a[i].clear();
	}
}

F

暴力harbin

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
 
using namespace std;
 
int const N = 7;
 
inline int ch_to_int(char c) {
	if (c == 'h')
		return 0;
	if (c == 'a')
		return 1;
	if (c == 'r')
		return 2;
	if (c == 'b')
		return 3;
	if (c == 'i')
		return 4;
	if (c == 'n')
		return 5;
	return 6;
}
 
int main() {
	static bool tot[7][7];
	static char s[6][2000003];
	int T;
	scanf("%d", &T);
	while (T--) {
		memset(tot, 0, sizeof(tot));
		for (int i = 0; i < 6; ++i) {
			scanf("%s", s[i]);
			for (int j = 0, m = strlen(s[i]); j < m; ++j)
				tot[i][ch_to_int(s[i][j])] = 1;
		}
		bool ans = 0;
		static int c[7];
		for (int i = 0; i < 6; ++i)
			c[i] = i;
		do {
			bool can = 1;
			for (int i = 0; i < 6; ++i)
				if (tot[i][c[i]] == 0) {
					can = 0;
					break;
				}
			if (can) {
				ans = 1;
				break;
			}
		} while (next_permutation(c, c + 6));
 
		if (ans)
			puts("Yes");
		else
			puts("No");
	}
}

G

H

I

可以倒过来考虑,每次把能固定的位置固定了,然后上下界移除的时候固定移除上界就行(此时答案乘2)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cassert>
 
using namespace std;
 
using ll = long long;
 
int const N = 100005;
int const MOD = 1e9 + 7;
 
inline ll KSM(ll a, ll k) {
	ll ret = 1;
	a = a % MOD;
	for (; k; k >>= 1, a = a * a % MOD)
		if (k & 1)
			ret = ret * a % MOD;
	return ret;
}
 
int fac[N];
int fac_inv[N];
int a[N];
int n;
 
inline void init() {
	fac[0] = 1, fac_inv[0] = 1;
	for (int i = 1; i < N; ++i)
		fac[i] = 1ll * fac[i - 1] * i % MOD;
	for (int i = 1; i < N; ++i)
		fac_inv[i] = KSM(fac[i], MOD - 2);
}
 
inline ll A(int n, int m) {
	return 1ll * fac[n] * fac_inv[n - m] % MOD;
}
 
int main() {
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);
	init();
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
			scanf("%d", &a[i]);
		int num = 0, c = 0, ans = 1;
		for (int i = n; i > 1; --i) {
			if (a[i] >= n || a[i - 1] > a[i]) {
				ans = 0;
				break;
			}
			if (a[i] == a[i - 1])
				++num;
			else {
				int minus = a[i] - a[i - 1] - 1;
				if (num < minus)
					ans = 0;
				ans = 1ll * ans * A(num, minus) % MOD;
				num -= minus;
				++c;
			}
		}
		ans = 1ll * ans * KSM(2, c) % MOD;
		if (a[1] || a[2] == 0 || a[n] != n - 1)
			ans = 0;
		printf("%d\n", ans);
	}
}

J

#include <iostream>
#include <cstdio>
#include <cmath>
 
using namespace std;
 
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int n;
		scanf("%d", &n);
		if (n <= 5)
			puts("-1");
		else {
			if (n & 1)
				printf("3 %d\n", n - 3);
			else
				printf("2 %d\n", n - 2);
		}
	}
}

K

直接按初始的情况平均分

#include <iostream>
#include <cstdio>
 
using namespace std;
 
int main() {
	static int a[100005];
	int T;
	scanf("%d", &T);
	while (T--) {
		int n, m;
		scanf("%d%d", &n, &m);
		long long sum = 0;
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			sum += a[i];
		}
		for (int i = 1; i <= n; ++i)
			printf("%.10f%c", (a[i] + 1.0 * a[i] * m / sum), (i == n ? '\n' : ' '));
	}
}

L

离线,对于每个状态建trie,那么只有成为一个前缀的时候才会成为解,注意特判一下后面有0的(在trie上多建一个点)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <map>
 
using namespace std;
 
int const N = 5005;
int const M = 2005;
 
struct trie_node {
	map<int, int> ch;
	bool can;
} trie[2000005];
int trie_cnt;
 
inline int trie_newNode() {
	++trie_cnt;
	trie[trie_cnt].ch.clear();
	trie[trie_cnt].can = 0;
	return trie_cnt;
}
 
int dfn[N];
int a[N];
int s[N];
 
int trie_insert(int len) {
	int p = 1;
	for (int i = 0; i < len; ++i) {
		if (trie[p].ch.count(s[i]) == 0)
			trie[p].ch[s[i]] = trie_newNode();
		p = trie[p].ch[s[i]];
	}
	return p;
}
 
int tot[N];
 
void clear_tot(int len) {
	for (int i = len; i >= 0; --i)
		tot[a[i]] = 0;
}
 
void trie_chk(int len) {
	int p = 1, num = 0;
	for (int i = len; i >= 0; --i) {
		if (tot[a[i]]) 
			continue;
		++num;
		tot[a[i]] = 1;
		if (trie[p].ch.count(a[i]))
			p = trie[p].ch[a[i]];
		else 
			break;
		trie[p].can = 1;
	}
	clear_tot(len);
}
 
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		trie_cnt = 0;
		trie_newNode();
 
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i)
			scanf("%d", &a[i]);
		for (int i = 1; i <= m; ++i) {
			int t;
			scanf("%d", &t);
			for (int j = 0; j < t; ++j)
				scanf("%d", &s[j]);
			while (t > 1 && s[t - 1] == 0 && s[t - 2] == 0)
				--t;
			dfn[i] = trie_insert(t);
		}
 
		for (int i = 0; i <= n; ++i)
			trie_chk(i);
 
		for (int i = 1; i <= m; ++i)
			if (trie[dfn[i]].can)
				puts("Yes");
			else
				puts("No");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值