神仙题1.0

灭虫

题目链接
【问题描述】
你需要在一条无限长的道路上喷洒杀虫剂。
在这条道路上,总共有 N 个投放点,其中第 i 个投放点在数轴上坐标 pi 处。在每一
个投放点,你可以选择往左喷洒或往右喷洒。但是由于风向和地理环境的影响,向左喷洒和
向右喷洒的效果不一定相同。具体来说,在一个位置向左喷洒,可以覆盖 [pi - li, pi] 这一段
区域,而向右喷洒可以覆盖 [pi, pi + ri] 这一段区域。
请你决定每个投放点是向左喷洒还是向右喷洒,来使得被杀虫剂覆盖的路段长度和最大。
【输入】
第一行 N。
接下来 N 行,每行 pi, li, ri。
【输出】
输出最大的被杀虫剂覆盖的长度和。

Input Output
4
1 2 2
3 3 3
4 3 3
6 2 2
Output
9

样例说明
让第一个和第三个投放点向左喷洒,其他投放点向右喷洒。
这样能覆盖的区域是 [-1,8],总长度为 9。
【数据说明】
对于测试点 1、 2, N≤ 15;
对于测试点 3、 4, N≤ 3,000, ri = 0;
对于测试点 5、 6, N≤ 30;
对于测试点 7、 8, N≤ 300;
对于测试点 9、 10, N≤ 3,000;
保证 pi, li, ri ≤ 10^9。
Code

#include<bits/stdc++.h>
using namespace std;
const int N = 3010, inf = 1e9 + 7;
int n;
struct node {
	int p, l, r;
	bool operator < (const node &z) const {
		return p < z.p;
	}
} a[N];
int b[N * 3], tot, f[N][N * 3], id[N * 3];
int main() {
	//freopen("kill.in", "r", stdin);
	//freopen("kill.out", "w", stdout);
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d%d%d", &a[i].p, &a[i].l, &a[i].r);
		a[i].l = a[i].p - a[i].l; a[i].r = a[i].p + a[i].r;
		b[++tot] = a[i].p; b[++tot] = a[i].l; b[++tot] = a[i].r;
	}
	sort(b + 1, b + 1 + tot); tot = unique(b + 1, b + 1 + tot) - b - 1;
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= n; i++) {
		a[i].p = lower_bound(b + 1, b + 1 + tot, a[i].p) - b;
		a[i].l = lower_bound(b + 1, b + 1 + tot, a[i].l) - b;
		a[i].r = lower_bound(b + 1, b + 1 + tot, a[i].r) - b;
		id[a[i].p] = i;
	}
    //f[i][j]表示前i个区间,贡献最右点小于等于j
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= tot; j++) f[i][j] = f[i - 1][j];
		for (int j = a[i].l + 1; j <= a[i].p; j++) 
            f[i][j] = max(f[i][j], f[i][j - 1] + b[j] - b[j - 1]);
        //i区间贡献(k,j]
		int mx = f[i - 1][a[i].p];
		for (int j = a[i].p + 1; j <= a[i].r; j++) {
			f[i][j] = max(f[i][j], mx + b[j] - b[a[i].p]);
            //i区间贡献(a[i].p,j]
			if (id[j] && a[id[j]].l < a[i].p) mx = max(mx, f[i - 1][a[id[j]].l] + b[a[i].p] - b[a[id[j]].l]);
		    //相交
        }
		for (int j = 2; j <= tot; j++) f[i][j] = max(f[i][j], f[i][j - 1]);
	}
	printf("%d\n", f[n][tot]);
	return 0;
} 

「ARC098F」Donation

题目链接
题目描述

给定一张 \(n\) 个点,\(m\) 条边的无向图。这张图的每个点有两个权值 \(a_i,b_i\)​。你将会从这张图中选出一个点作为起点,随后开始遍历这张图。你能到达一个节点 \(i\) 当且仅当你的手上有至少 \(a_i\)​ 元钱。当你到达一个节点 \(i\) 后,你可以选择对这个点捐赠 \(b_i\)​ 元。你需要对每个点捐赠一次。问你身上至少要带多少元钱?
Code

#include<bits/stdc++.h>

#define int long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
	x = 0; RG char c = getchar(); bool f = 0;
	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
	x = f ? -x : x;
	return ;
}
template<class T> inline void write(T x) {
	if (!x) {putchar(48);return ;}
	if (x < 0) x = -x, putchar('-');
	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 1e5 + 10;
vector<int> e[N];
int n, m, a[N], b[N], p[N], f[N], fa[N], Sum[N];
bool vis[N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
bool cmp(int x, int y) { return a[x] < a[y]; }
 main() {
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	read(n), read(m);
	for (int i = 1; i <= n; i++) {
		read(a[i]), read(b[i]);
		a[i] = max(a[i] - b[i], 0ll);
		p[i] = i, fa[i] = i;
	}
	for (int i = 1, x, y; i <= m; i++) {
		read(x), read(y);
		e[x].push_back(y); e[y].push_back(x);
	}
	sort(p + 1, p + 1 + n, cmp);
	for (int i = 1; i <= n; i++) {
		int u = p[i]; vis[u] = 1;
		Sum[u] = b[u];
		vector<int> son;
		for (auto v : e[u]) {
			if (!vis[v] || find(v) == u) continue;
			Sum[u] += Sum[find(v)];
			son.push_back(find(v));
			fa[find(v)] = u;
		}
		f[u] = Sum[u] + a[u];
		for (auto v : son) f[u] = min(f[u], max(a[u], f[v]) + Sum[u] - Sum[v]); 
	}
	printf("%lld\n", f[p[n]]);
	return 0;
}

AT2294 Eternal Average

【题目链接】
luogu
atcoder

题目描述
黑板上有\(n\)个0和\(m\)个1,我们每次选择\(k\)个数字将其擦除,然后把它们的平均数写上去,这样一直操作直到只剩下一个数字,问剩下的这个数字有多少种不同的情况。

答案对\(10^9+7\)取模

\(1 \leq n,m \leq 2000,2 \leq k \leq 2000\)

保证\(n+m-1\)能被\(k\)整除。

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> T gi() {
	T x = 0; bool f = 0; char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = 1, c = getchar();
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f ? -x : x;
}
const int N = 2010, Mod = 1e9 + 7;
int f[N << 1][N][2], n, m, k, ans, sum[N];
int main() {
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	f[0][0][0] = 1;
	n = gi<int>(), m = gi<int>(), k = gi<int>();
	for (int i = 1; i <= n + m; i++) {
		for (int j = 0; j <= m; j++) {
			f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]) % Mod;
			sum[j] = ((j ? sum[j - 1] : 0) + f[i][j][0]) % Mod;
			if (j) f[i][j][1] = (sum[j - 1] - (j - k < 0 ? 0 : sum[j - k]) + Mod) % Mod;
		}
		for (int j = 0; j <= m; j++)
			if (j % (k - 1) == m % (k - 1) && (k - 1) * i - j + 1 <= n &&
				((k - 1) * i - j + 1) % (k - 1) == n % (k - 1))
				ans = (ans + f[i][j][1]) % Mod;
	}
	printf("%d\n", ans);
	return 0;
}

【BZOJ3711】Druzyny

题目链接

Description
体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。
第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。

Code

#define lson (o << 1)
#define rson (o << 1 | 1)
int Max[N << 2], c[N], d[N], n;
void Build(int o, int l, int r) {
	if (l == r) return (void) (Max[o] = l);
	int mid = (l + r) >> 1;
	Build(lson, l, mid); Build(rson, mid + 1, r);
	Max[o] = c[Max[lson]] > c[Max[rson]] ? Max[lson] : Max[rson];
	return ;
}
int Qpos(int o, int l, int r, int L, int R) {
	if (L <= l && r <= R) return Max[o];
	int mid = (l + r) >> 1, res = 0;
	if (L <= mid) res = Qpos(lson, l, mid, L, R);
	if (R > mid) {
		int t = Qpos(rson, mid + 1, r, L, R);
		res = c[res] > c[t] ? res : t;
	}
	return res;
}
struct data {
	int v, c;
	data operator + (const data &z) const {
		if (v == z.v) return (data) {v, (c + z.c) % Mod};
		else if (v > z.v) return (data) {v, c};
		else return z;
	}
} f[N], t[N << 2], lz[N << 2];
void pushup(int o) { t[o] = t[lson] + t[rson]; }
void put(int o, data k) { lz[o] = lz[o] + k, t[o] = t[o] + k; }
void pushdown(int o) {
	if (lz[o].v != -inf) {
		put(lson, lz[o]), put(rson, lz[o]);
		lz[o] = (data) {-inf, 0};
	}
}
void build(int o, int l, int r) {
	lz[o] = (data) {-inf, 0};
	if (l == r) return (void) (t[o] = f[l]);
	int mid = (l + r) >> 1;
	build(lson, l, mid); build(rson, mid + 1, r);
	return pushup(o);
}
void Modify(int o, int l, int r, int L, int R, data k) {
	if (L <= l && r <= R) return put(o, k);
	int mid = (l + r) >> 1; pushdown(o);
	if (L <= mid) Modify(lson, l, mid, L, R, k);
	if (R > mid) Modify(rson, mid + 1, r, L, R, k);
	return pushup(o);
}
data query(int o, int l, int r, int L, int R) {
	if (L <= l && r <= R) return t[o];
	int mid = (l + r) >> 1; data res = (data) {-inf, 0}; pushdown(o);
	if (L <= mid) res = query(lson, l, mid, L, R);
	if (R > mid) res = res + query(rson, mid + 1, r, L, R);
	return res;
}
int L[N];
void prework() {
	for (int i = 1; i <= n; i++) f[i] = (data) {-inf, 0};
	f[0] = (data) {0, 1};
	Build(1, 1, n), build(1, 0, n);
	priority_queue<int, vector<int>, greater<int> > q1, q2;
	for (int i = 1; i <= n; i++) {
		L[i] = L[i - 1], q1.push(d[i]);		
		while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();
		while (q1.top() < i - L[i]) {
			q2.push(d[++L[i]]);		
			while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();
		}
	}
	return ;
}
void upd(int p) {
	int o = 1, l = 0, r = n;
	while (l < r) {
		int mid = (l + r) >> 1;
		pushdown(o);
		if (p <= mid) o = lson, r = mid;
		else o = rson, l = mid + 1;
	}
	t[o] = f[p] = f[p] + lz[o];
	while (o >>= 1) pushup(o);
}
void solve(int l, int r) {
	if (l == r) return upd(l);
	int p = Qpos(1, 1, n, l + 1, r);
	solve(l, p - 1);
	int i = max(p, l + c[p]);
	data now = (l <= i - c[p] - 1 ? query(1, 0, n, l, i - c[p] - 1) : (data) {-inf, 0});
	while (i <= r && L[i] <= l && i - c[p] < p) {
		now = now + f[i - c[p]];
		f[i] = f[i] + (data) {now.v + 1, now.c};
		i++;
	}
	if (i <= r && L[i] <= l) {
		int ll = i, rr = r;
		while (ll <= rr) {
			int mid = (ll + rr) >> 1;
			if (L[mid] <= l) ll = mid + 1;
			else rr = mid - 1;
		}
		Modify(1, 0, n, i, ll - 1, (data) {now.v + 1, now.c});
		i = ll;
	}
	while (i <= r && L[i] > l && L[i] < p) {
		data res = query(1, 0, n, L[i], min(i - c[p], p - 1));
		f[i] = f[i] + (data) {res.v + 1, res.c};
		i++;
	}
	solve(p, r);
	return ;
}
int main() {
	n = gi<int>();
	for (int i = 1; i <= n; i++)
		c[i] = gi<int>(), d[i] = gi<int>();
	prework();
	solve(0, n);
	if (f[n].v <= 0) puts("NIE");
	else printf("%d %d\n", f[n].v, f[n].c);
	return 0;
}

「JOISC 2020 Day2」遗迹

题目链接

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> T gi() {
	T x = 0; bool f = 0; char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = 1, c = getchar();
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f ? -x : x;
}
const int N = 650, Mod = 1e9 + 7;
int f[N << 1][N], g[N][N];
bool a[N << 1];
int fpow(int a, int b) {
	int res = 1;
	for (; b; b >>= 1, a = 1ll * a * a % Mod)
		if (b & 1) res = 1ll * res * a % Mod;
	return res;
}
int fac[N], ifac[N];
int C(int n, int m) {
	if (n < m) return 0;
	return 1ll * fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
}
int main() {
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	int n = gi<int>();
	for (int i = 1; i <= n; i++) a[gi<int>()] = 1;
	fac[0] = g[0][0] = 1;
	for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % Mod;
	ifac[n] = fpow(fac[n], Mod - 2);
	for (int i = n; i; i--) ifac[i - 1] = 1ll * ifac[i] * i % Mod;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= i; j++) {
			g[i][j] = g[i - 1][j];
			if (j) (g[i][j] += 2ll * g[i - 1][j - 1] * j % Mod) %= Mod;
			if (j > 1) (g[i][j] += 1ll * g[i - 1][j - 2] * (j - 1) % Mod * j % Mod) %= Mod;
		}
	int cnt0 = 0, cnt1 = 0;
	f[2 * n + 1][0] = 1;
	for (int i = 2 * n; i; i--)
		if (!a[i]) {
			for (int j = cnt0; j <= cnt1; j++)
				f[i][j] = 1ll * f[i + 1][j] * (j - cnt0) % Mod;
			cnt0++;
		}
		else {
			for (int j = 0; j <= cnt1; j++) {
				(f[i][j] += f[i + 1][j]) %= Mod;
				for (int k = 1; j + k <= cnt1 + 1; k++)
					(f[i][j + k] += 1ll * f[i + 1][j] *
					 C(cnt1 - j, k - 1) % Mod * g[k - 1][k - 1] % Mod * (k + 1) % Mod) %= Mod;
			}
			cnt1++;
		}
	cout << 1ll * f[1][n] * fpow(fpow(2, n), Mod - 2) % Mod << endl;
	return 0;
}

posted @ 2019-09-21 22:43  zzy2005  阅读(303)  评论(0编辑  收藏  举报