组队赛总结

11 篇文章 0 订阅
4 篇文章 0 订阅

DIFERENCIJA

求一个数组所有连续子序列的最大值减最小值的权值总和
先转化为求所有连续子序列的最大值和所有连续子序列的最小值

采用 d p dp dp加栈的想法, d p 1 [ i ] dp1 [ i ] dp1[i]代表以 i i i为右端点的前 i i i个区间的最大值
状态转移 d p 1 [ i ] = d p 1 [ p o s ] + ( i − p o s ) ∗ a [ i ] dp1[i] = dp1[pos] + (i - pos)*a[i] dp1[i]=dp1[pos]+(ipos)a[i] p o s pos pos代表前一个大于 a [ i ] a[i] a[i]的位置 用一个模拟栈来实现找 p o s pos pos

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 100;
ll a[maxn], ans;
ll maxx, minn;
ll smaxx[maxn], sminn[maxn];
ll dp1[maxn], dp2[maxn], sum;
int main() {
	int n, m;
	cin >> n;
	for (ll i = 1; i <= n; i++) {
		cin >> a[i];
		while (maxx && a[smaxx[maxx]] > a[i]) maxx--;
		while (minn && a[sminn[minn]] < a[i]) minn--;

		ll pos = sminn[minn];
		dp1[i] = dp1[pos] + (i - pos)*a[i];
		//	cout << 1 << " " << i << " " << pos << ' ' << (i - pos)*a[i] << ' ' << ans1[pos] << " " << ans1[i] << endl;

		pos = smaxx[maxx];
		dp2[i] = dp2[pos] + (i - pos)*a[i];
		//cout << 2 << " " << i << " " << pos << ' ' << (i - pos)*a[i] << ' ' << ans2[pos] << " " << ans2[i] << endl;
		sum += dp1[i] - dp2[i];
		smaxx[++maxx] = i;
		sminn[++minn] = i;
	}
	cout << sum << endl;
	return 0;
}

HONI

这道题是场上队友秒掉的%%tql
a [ i ] a[i] a[i]个数代表困难度为 i i i的题目的数量 第 b [ i ] b[i] b[i]代表困难度为 i i i或者 i + 1 i+1 i+1的题目数量
需要各种难度的题目各一道,要求会有几种方案数
用一个二维 d p [ i ] [ 1 ] dp[i][1] dp[i][1]代表第 i i i种难度不选择 b [ i ] b[i] b[i]的方案数 用 d p [ i ] [ 0 ] dp[i][0] dp[i][0]代表第 i i i种难度选择 b [ i ] b[i] b[i]的方案数 最后答案贡献为 d p [ n ] [ 1 ] + d p [ n ] [ 0 ] dp[n][1]+dp[n][0] dp[n][1]+dp[n][0]
递推方程为 d p [ i ] [ 1 ] = ( d p [ i − 1 ] [ 1 ] ∗ ( a [ i ] + b [ i − 1 ] ) + d p [ i − 1 ] [ 0 ] ∗ ( a [ i ] + m a x ( n u m , b [ i − 1 ] − 1 ) ) ) dp[i][1] = (dp[i - 1][1] * (a[i] + b[i - 1]) + dp[i - 1][0] * (a[i] + max(num, b[i-1] - 1)))%mod dp[i][1]=(dp[i1][1](a[i]+b[i1])+dp[i1][0](a[i]+max(num,b[i1]1))) d p [ i ] [ 0 ] = b [ i ] ∗ ( d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] ) dp[i][0] = b[i] * (dp[i - 1][0] + dp[i - 1][1]) % mod dp[i][0]=b[i](dp[i1][0]+dp[i1][1])

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 100;
ll a[maxn], ans;
ll b[maxn];
ll dp[maxn][3];
ll num;
const int mod = 1e9 + 7;
int main() {
	ll n, m;
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	for (int i = 1; i < n; i++) cin >> b[i];
	dp[1][1] = a[1]; dp[1][0] = b[1];
	for (int i = 2; i <= n; i++) {
		dp[i][1] = (dp[i - 1][1] * (a[i] + b[i - 1]) + dp[i - 1][0] * (a[i] + max(num, b[i-1] - 1)))%mod;
		dp[i][0] = b[i] * (dp[i - 1][0] + dp[i - 1][1]) % mod;
	}
	cout << (dp[n][1] + dp[n][0]) % mod << endl;
	return 0;
}

DVONIZ

求前 k k k元素和后 k k k个元素的和都不大于 S S S时,则认为这个序列是有趣的,找出从每个 i i i开始的最长的有趣的子序列
开始想用二分写 仔细一想这里的区间增加不是完全单调的,但是如果枚举中间点的话同时向两边扩张,这样的区间一定是单调的,找出每个 i i i向两边扩张的最大值,记下来,最后输出的时候再取一个max就好了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100;
const int mod = 1e9 + 7;

ll l[maxn], r[maxn];
ll a[maxn], sum[maxn];
ll ans[maxn];
int main() {
	ios::sync_with_stdio; cin.tie(0); cout.tie(0);
	ll n, s;
	cin >> n >> s;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
	}
	for (int i = 1; i <= n; i++) {
		l[i] = l[i - 1];
		r[i] = r[i - 1];
		while (l[i] <= n && sum[i - 1] - sum[l[i] - 1]>s)l[i]++;
		while (r[i] + 1 <= n && sum[r[i] + 1] - sum[i - 1] <= s)r[i]++;
		ll anss = min(i - l[i], r[i] - i + 1);
		ans[i - anss] = max(anss, ans[i - anss]);
	}
	for (int i = 1; i <= n; i++) {
		ans[i] = max(ans[i], ans[i - 1] - 1);
		cout << ans[i] * 2 << endl;
	}
	return 0;
}

POŠTAR

找到一条最大值减最小值最小的路,一看范围 n n n才50,那就直接暴搜了,然后加一点优化,固定最小值找满足条件的最小的最大值,那就敲一个二分就好了,比赛敲的时候忘记把开始点的权值也打进去(wa了一发哭哭)

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn = 60;
const int N = 6000;
int sx, sy;
int cnt;
char mp[maxn][maxn];
int vis[maxn][maxn];
int b[N];
int dx[8] = { 0,0,1,1,1,-1,-1,-1 };
int dy[8] = { 1,-1,1,0,-1,1,0,-1 };
int a[maxn][maxn];
int ans = 0;
void dfs(int x, int y, int l, int r) {
	for (int i = 0; i <= 7; i++) {
		int xx = x + dx[i];
		int yy = y + dy[i];
		if (xx >= 1 && xx <= n && yy >= 1 && yy <= n && !vis[xx][yy] && a[xx][yy] >= l && a[xx][yy] <= r) {
			if (mp[xx][yy] == 'K') ans++;
			vis[xx][yy] = 1;
			dfs(xx, yy, l, r);
		}
	}
}
bool check(int l, int r) {
	ans = 0;
	memset(vis, 0, sizeof vis);
	vis[sx][sy] = 1;
	dfs(sx, sy, l, r);
	if ( a[sx][sy]<l || a[sx][sy]>r ) return 0;
	//cout <<l<<" "<<r<<" "<<cnt<<" "<< ans << endl;
	return ans == cnt;
}
int main() {
	ios::sync_with_stdio; cin.tie(0); cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> mp[i][j];
			if (mp[i][j] == 'P') {
				sx = i; sy = j;
			}
			if (mp[i][j] == 'K') cnt++;
		}
	}
	int maxx = 0; int minn = 1e9;
	int cnt1 = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> a[i][j];
			if (mp[i][j] == 'P' || mp[i][j] == 'K') {
				maxx = max(maxx, a[i][j]);
				minn = min(minn, a[i][j]);
			}
			b[++cnt1] = a[i][j];
		}
	}
	sort(b + 1, b + cnt1 + 1);
	int anss = 1e9;
	for (int i = 1; i <= cnt1; i++) {
		int l = b[i]; int r = b[cnt1]+1;
	//	cout << l << " " << r << endl;
		while (l < r) {
			int mid = (l + r) >> 1;
		//	cout << mid << endl;
			if (check(b[i], mid)) {
				r = mid;
				anss = min(anss, r - b[i]);
			}
			else {
				l = mid + 1;
			}
		}	
		if (b[i] == minn) break;
	}
	cout << anss << endl;
	return 0;
}
/*
2
P.
.K
81 1
1 1
*/

KUGLICE

有一个有向图,其中每个点的出度为0或者1,有以下询问:
1 x x x:给定弹珠的起始点,求弹珠运动的终止点
2 x x x 删除 x x x点的出边
反向思考先构建出最后删完边的图 然后存起来记下来 再一点点加边 输出就好了

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 100;
bool vis[maxn];
int  fa[maxn], root[maxn],son[maxn];
int find(int x) { return root[x] == x ? x : root[x] = find(root[x]); }
int n;
struct node {
	int op, x;
}a[maxn];
int ans[maxn];
int main() {
	cin >> n;
	for (int i = 1; i <= n+1; i++) root[i] = i;
	for (int i = 1; i <= n; i++) {
		int x; cin >> x;
    	if (x == 0) {
			vis[i] = 1;
		}
		son[i] = x;
	}
	int q; cin >> q;
	for (int i = 1; i <= q; i++) {
		int op, x;
		cin >> op >> x;
		if (op == 2) vis[x] = 1;
		a[i].op = op; a[i].x = x;
	}
	for (int i = 1; i <= n; i++) {
		if (!vis[i]) {
			int fx = find(i);
			int fy = find(son[i]);
			if (fx == fy) root[fx] = root[fy] = n + 1;
			else root[fx] = fy;
		}
	}
	int cnt = 0;
	for (int i = q; i >= 1; i--) {
		if (a[i].op == 2) {
			int fx = find(a[i].x);
			int fy = find(son[a[i].x]);
			if (fx == fy) root[fx] = root[fy] = n + 1;
			else root[fx] = fy;
		}
		else ans[++cnt] = find(a[i].x);
	}
	for (int i = cnt; i >= 1; i--) {
		if (ans[i] == n + 1) cout << "CIKLUS" << endl;
		else cout << ans[i] << endl;
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值