Codeforces round #321 (DIV. 2)

博客包含了Codeforces Round #321 分数赛第二组的五道题目解析,分别是:A题‘Kefa and First Steps’的非递减子序列问题;B题‘Kefa and Company’的双关键字数组最大和问题,采用排序和双指针方法解决;C题‘Kefa and Park’要求求解最短路径上连续1不超过m个的叶子节点数量,通过DFS解决;D题‘Kefa and Dishes’涉及状压DP解决选择节点以最大化权和的问题;E题‘Kefa and Watch’则需要处理字符串周期性和查询,利用动态区间Hash值和线段树来解答。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解链接:http://www.cygmasot.com/index.php/2015/09/23/codeforces_580/

链接

A:Kefa and First Steps

题意:the length of the maximum non-decreasing subsegment

直接搞,水题

#include <cstring> 
#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <map>
#include <queue>
#include <set>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}  
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 1e9;
const int N = 1e5+10;
int n; 
int a[N];
int main() {
	rd(n);
	for (int i = 1; i <= n; i++)rd(a[i]);
	int ans = 1;
	for (int i = 2, pre = 1; i <= n; i++) {
		if (a[i] >= a[i - 1]) {
			pre++;
		}
		else pre = 1;
		ans = max(ans, pre);
	}pt(ans);
	return 0;
}


B:Kefa and Company

题意:有一个双关键字的数组,共n个元素。常数d
选任意个使得选出的元素中 第一关键字(最大的-最小的)<d
并且使得第二关键字和最大。
问:最大的和是多少。
思路:
先排个序,然后two pointers搞搞,枚举左端点,右端点是单调的。

#include <cstring> 
#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <map>
#include <queue>
#include <set>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}  
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 1e9;
const int N = 1e5+10;
int n, d;
pii a[N];
ll sum[N];
int main() {
	rd(n); rd(d);
	for (int i = 1; i <= n; i++) {
		rd(a[i].first); rd(a[i].second);
	}
	sort(a + 1, a + 1 + n);
	sum[0] = 0;
	for (int i = 1; i <= n; i++)sum[i] = sum[i - 1] + a[i].second;
	ll ans = a[1].second;
	int r = 1;
	for (int i = 1; i <= n; i++) {
		while (r+1 <= n && a[r+1].first - a[i].first < d)r++;
		ans = max(ans, sum[r] - sum[i - 1]);
	}
	pt(ans);
	return 0;
}


C:Kefa and Park

题意:给定n个点的数 常数m

下面n个数给出点权(点权为0或1)

要求从根走到叶子节点的最短路径上 连续1的个数不超过m个 的叶子节点有多少个。

思路:

dfs直接搞就好了。

#include <cstring> 
#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <map>
#include <queue>
#include <set>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}  
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 1e9;
const int N = 1e5+10;
int n, m;
int a[N];
vector<int>G[N];
int ans;
void dfs(int u, int fa, int val) {
	if(a[u] == 1)
	val += a[u];
	else val = 0;
	if (val > m)return;
	if (u != fa && G[u].size() == 1)
	{
		ans++;
	}
	for (auto v : G[u]) {
		if (v == fa)continue;
		dfs(v, u, val);
	}
}
int main() {
	rd(n); rd(m);
	for (int i = 1; i <= n; i++)rd(a[i]);
	for (int i = 1, u, v; i < n; i++) {
		rd(u); rd(v);
		G[u].push_back(v); G[v].push_back(u);
	}
	ans = 0;
	dfs(1, 1, 0);
	pt(ans);
	return 0;
}


D:Kefa and Dishes

n个点 m条有向边 选恰好k个点

下面n个数给出点权。

下面m行给出边和边权。

设上一次选的点是u

这一次选的点是v,则可以获得边权u->v的价值。

问:使得选出的权和最大,问最大的权和。

思路:显然是状压dp

dp[i][j]表示选的点状态为i,最后一次选的点是j的最大价值。

#include <cstring> 
#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <map>
#include <queue>
#include <set>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}  
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 1e9;
const int N = 20;
int n, m, k;
int cnt(int x) {
	int ans = 0;
	while (x) { ans += x & 1; x >>= 1; }return ans;
}
int a[N];
int b[N][N];
ll dp[1 << 18][18];
bool vis[1 << 18][18];
int main() {
	rd(n); rd(m); rd(k);
	for (int i = 0; i < n; i++)rd(a[i]);
	while (k--) {
		int u, v, d;
		rd(u); rd(v); rd(d); b[u-1][v-1] = d;
	}
	queue<pii> q;
	for (int i = 0; i < n; i++) {
		dp[1 << i][i] = a[i];
		q.push({ 1 << i,i });
		vis[1 << i][i] = 1;
	}
	ll ans = 0;
	while (!q.empty()) {
		auto u = q.front(); q.pop();
		if (cnt(u.first) == m) {
			ans = max(ans, dp[u.first][u.second]);
			continue;
		}
		for (int i = 0; i < n; i++) {
			if (!(u.first & (1 << i)))
			{
				pii v = { u.first | (1 << i), i };
				ll now = dp[u.first][u.second] + b[u.second][i] + a[i];
				dp[v.first][v.second] = max(dp[v.first][v.second], now);
				if (!vis[v.first][v.second]) {
					vis[v.first][v.second] = 1;
					q.push(v);
				}
			}
		}
	}
	pt(ans);
	return 0;
}


E:Kefa and Watch

题意:

给定n m k。

下面长度为n的数字串。

下面m+k个操作。

1 l r c:把区间[l,r]的所有字符改成c

2 l r d:询问区间[l,r]是否为周期d

(所谓周期 x 就是 (1 ≤ x ≤ |s|), if si  =  si + x for all i from 1 to |s|  -  x.)

对于每个询问输出YES or NO

思路:

首先是一个结论:若字符串s(下标为:[1,len])具有周期性x

那么 [1+x,len] == [1,len-x]

这个可以简单证明得到,不再重复证明。

因此我们需要动态获得区间的Hash值。

所以用线段树维护区间Hash值.

 

#include <cstring> 
#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <map>
#include <queue>
#include <set>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}  
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 1e8;
const int MAGIC = 311, MOD = 1e9 + 7;
const int N = 1e5 + 10;
#define L(x) tree[x].l
#define R(x) tree[x].r
#define ls (id<<1)
#define rs (id<<1|1)
#define H(x) tree[x].hash
#define La(x) tree[x].lazy
#define Len(x) tree[x].len
int base[N];
int p[10][N];
int a[N];
struct Node {
	int l, r, hash, lazy, len;
}tree[N << 2];
void up(int id) {
	H(id) = ((ll)H(ls) * base[Len(rs)] % MOD + H(rs)) % MOD;
}
void go(int id, int val) {
	La(id) = val;
	H(id) = p[val][Len(id)];
}
void down(int id) {
	if (La(id) != -1) {
		go(ls, La(id));
		go(rs, La(id));
		La(id) = -1;
	}
}
void build(int l, int r, int id) {
	L(id) = l; R(id) = r; Len(id) = r - l + 1; 
	La(id) = -1;
	if (l == r) {
		H(id) = p[a[l]][1];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, ls); build(mid + 1, r, rs); 
	up(id);
}
void change(int l, int r, int val, int id) {
	if (l == L(id) && R(id) == r) {
		go(id, val);
		return;
	}
	down(id);
	int mid = (L(id) + R(id)) >> 1;
	if (r <= mid)
		change(l, r, val, ls);
	else if (mid < l)
		change(l, r, val, rs);
	else {
		change(l, mid, val, ls);
		change(mid + 1, r, val, rs);
	}
	up(id);
}
int query(int l, int r, int id) {
	if (l == L(id) && R(id) == r) {
		return H(id);
	}
	down(id);
	int mid = (L(id) + R(id)) >> 1;
	int ok;
	if (r <= mid)
		ok = query(l, r, ls);
	else if (mid < l)
		ok = query(l, r, rs);
	else {
		ok = query(l, mid, ls);
		ok = ((ll)ok * base[r-mid] % MOD + query(mid + 1, r, rs)) % MOD;
	}
	up(id);
	return ok;
}
int n, m, k;

char str[N];
int main() {
	base[0] = 1;
	for (int i = 1; i < N; ++i) base[i] = ((ll)base[i - 1] * MAGIC) % MOD;
	for (int i = 0; i < 10; i++) {
		p[i][0] = 0;
		for (int j = 1; j < N; j++)p[i][j] = ((ll)p[i][j - 1] * MAGIC%MOD + i) % MOD;
	}
	rd(n); rd(m); rd(k);
	scanf("%s", str + 1);
	for (int i = 1; i <= n; i++)a[i] = str[i]-'0';
	build(1, n, 1);
	k += m;
	while (k--) {
		int op, l, r, c;
		rd(op); rd(l); rd(r); rd(c);
		if (op == 1) 
			change(l, r, c, 1);
		else {
			if (l > r-c || l+c>r) { puts("YES");continue; }
			ll x = query(l, r - c, 1), y = query(l + c, r, 1);
			x == y ? puts("YES") : puts("NO");
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值