2023年ICPC合肥站 补题记录

B. Queue Sorting(计数dp)

#include <bits/stdc++.h>

using namespace std;

// #define int long long
#define double long double
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 2e5 + 10;
const int maxn = 1e6;
const int MAXN = 100;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n; cin >> n;
	vector<int> a(n + 1), pre(n + 1);
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		pre[i] = pre[i - 1] + a[i];
	}

	vector<vector<int>> c(pre[n] + 1, vector<int>(pre[n] + 1));
	c[0][0] = 1;
	for (int i = 1; i <= pre[n]; i++)
	{
		c[i][0] = 1;
		for (int j = 1; j <= i; j++)
		{
			c[i][j] = (c[i][j] + c[i - 1][j - 1] + c[i - 1][j]) % mod;
		}
	}

	vector<vector<int>> dp(n + 1, vector<int>(pre[n] + 1));
	dp[0][0] = 1;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= pre[i]; j++) // 第i个位置最末的下降子序列开头
		{
			if (dp[i][j])
			{
				dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod;
				for (int k = j + 1; k < pre[i + 1]; k++) // 把i+1都放在[j+1,k]
				{
					// 枚举多少i+1放在末尾
					int minn = max(0, a[i + 1] - (k - j));
					int maxx = min(a[i + 1] - 1, pre[i + 1] - k - 1);
					for (int x = minn; x <= maxx; x++)
						dp[i + 1][k] = (dp[i + 1][k] + (1ll * dp[i][j] * c[k - j - 1][a[i + 1] - x - 1] % mod)) % mod;
				}
			}
		}
	}

	int ans = 0;
	for (int i = 0; i <= pre[n]; i++) ans = (ans + dp[n][i]) % mod;
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}

C. Cyclic Substrings(回文自动机)

  • 将原字符串复制一遍,防止重复,结尾大于 n n n 的才加入cnt中
  • 之后就是回文自动机的板子
#include <bits/stdc++.h>

using namespace std;

// #define int long long
#define double long double
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 6e6 + 10;
const int maxn = 1e6;
const int MAXN = 100;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

	int ans = 0;
/*
---------------回文自动机PAM---------------

- 传入字符串下标从0开始
- 本质不同的回文子串个数
- 所有回文子串个数
- 每种回文串出现的次数 cnt(需要get_cnt)
- 每种回文串的长度 len
- 以下标 i 为结尾的回文串的个数 sed
- 每个回文串在原串中出现的起始位置 record
*/

struct PAM {
	string s;
	int n;
	int nxt[N][10];
	int fail[N]; // 当前节点最长回文后缀的节点
	int len[N]; // 当前节点表示的回文串的长度
	int cnt[N]; // 当前节点回文串的个数, 在getcnt后可得到全部
	// int sed[N]; // 以当前节点为后缀的回文串的个数
	// int record[N]; // 每个回文串在原串中出现的位置
	int tot; // 节点个数
	int last; // 上一个节点
	void init()
	{
		tot = 0;
		for (int i = 0; i < N; i ++ )
		{
			fail[i] = cnt[i] = len[i] = 0;
			for (int j = 0; j < 10; j ++ ) nxt[i][j] = 0;
		}
	}
	int newnode(int lenx)
	{
		for (int i = 0; i < 26; i++)
		nxt[tot][i] = 0;
		cnt[tot] = 0;
		len[tot] = lenx;
		return tot;
	}
	void build(string ss)
	{
		tot = 0;
		newnode(0);
		tot = 1, last = 0;
		newnode(-1);
		fail[0] = 1;
		n = ss.size();
		s = " " + ss;
	}
	int getfail(int x, int n)
	{
		while (n - len[x] - 1 <= 0 || s[n - len[x] - 1] != s[n])
			x = fail[x];
		return x;
	}
	void insert(char cc, int pos, int op)
	{
		int c = cc - '0';
		int p = getfail(last, pos);
		if (!nxt[p][c])
		{
			tot++;
			newnode(len[p] + 2);
			fail[tot] = nxt[getfail(fail[p], pos)][c];
			len[tot] = len[p] + 2;
			// sed[tot] = sed[fail[tot]] + 1;
			nxt[p][c] = tot;
		}
		last = nxt[p][c];
		cnt[last] += op;
		// record[last] = pos;
	}
	void insert()
	{
		for (int i = 1; i <= n; i++)
		{
			if (i <= n / 2) insert(s[i], i, 0);
			else insert(s[i], i, 1);
		}
	}
	void get_cnt()
	{
		for (int i = tot; i > 0; i -- )
		{
			cnt[fail[i]] += cnt[i];
			if (i >= 2 && len[i] <= n / 2)
			{
				ans = (ans + 1ll * cnt[i] * cnt[i] % mod * len[i] % mod) % mod;
			}
		}
	}
	int get_diff_cnt() // 本质不同的回文子串个数
	{
		return tot - 1;
	}
	int get_all_cnt() // 所有回文子串个数(本质相同的多次计算)
	{
		int sum = 0;
		get_cnt();
		for (int i = 2; i <= tot; i ++ ) sum += cnt[i];
		return sum;
	}
} pam;
//------------------------------------

void solve()
{
	int n; cin >> n;
	string s; cin >> s;
	s = s + s;
	pam.init();
	pam.build(s);
	pam.insert();
	pam.get_cnt();
	cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

E. Matrix Distances(前缀和)

  • 分别统计横坐标和纵坐标的贡献,统计方式是用前缀和
  • 坑点是需要离散化,直接map会T
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e3 + 10;
const int maxn = 1e6;
const int MAXN = 100;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

int idx;
int g[N][N];

void solve()
{
	int n, m; cin >> n >> m;
	vector<int> tt;
	map<int, int> mp;
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= m; j ++ )
		{
			cin >> g[i][j];
			tt.push_back(g[i][j]);
		}
	}
	sort(tt.begin(), tt.end());
	tt.erase(unique(tt.begin(), tt.end()), tt.end());
	for (int i = 0; i < tt.size(); i ++ )
	{
		mp[tt[i]] = i + 1;
	}
	vector<vector<int>> x(tt.size() + 10), y(tt.size() + 10);
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= m; j ++ )
		{
			int tmp = mp[g[i][j]];
			x[tmp].push_back(i);
			y[tmp].push_back(j);
		}
	}

	int ans = 0;
	for (auto t : x)
	{
		if (t.size() <= 1) continue;
		sort(t.begin(), t.end());
		vector<int> pre(t.size() + 2);
		pre[0] = t[0];
		for (int i = 1; i < t.size(); i ++ )
		{
			pre[i] = pre[i - 1] + t[i];
			ans += t[i] * (i + 1) - pre[i];
		}
	}
	for (auto t : y)
	{
		if (t.size() <= 1) continue;
		sort(t.begin(), t.end());
		vector<int> pre(t.size() + 2);
		pre[0] = t[0];
		for (int i = 1; i < t.size(); i ++ )
		{
			pre[i] = pre[i - 1] + t[i];
			ans += t[i] * (i + 1) - pre[i];
		}
	}
	cout << ans * 2 << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

F. Colorful Balloons(签到)

  • 没看题,偷一下队友的码
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include <iomanip>
#include<random>
#include<set>

using namespace std;
typedef long long LL;
typedef __int128 i128;
typedef unsigned long long ULL;
typedef double db;

void Asuka()
{
	int n;
	map<string , int>mp;
	cin >> n;
	for(int i = 1 ; i <= n ; i ++){
		string s;
		cin >> s;
		mp[s] ++;
	}
	for(auto t : mp){
		if(t.second > n / 2){
			cout << t.first << '\n';
			return;
		}
	}
	cout << "uh-oh\n";
	return;
}	

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	int t = 1;
	// cin >> t;
	while(t --){
		Asuka();
	}
	return 0;
}

G. Streak Manipulation(二分+dp)

  • 首先这种最大最小值的问题很容易想到二分,我们二分最终答案,也就是第 k 长连续 1 子段的最大长度
  • 怎么实现 check 函数呢,看到 k 最大为 5 ,考虑能不能用dp
  • dp[i][j][0/1] 表示前 i 个字符中有 j 段长度大于等于 mid 的连续 1 子串且第 i 位为 0/1 的最小操作数
  • 更新方式:
    • s [ i ] = ′ 0 ′ s[i]='0' s[i]=0
      d p [ i ] [ j ] [ 0 ] = min ⁡ ( d p [ i ] [ j ] [ 0 ] , d p [ i − 1 ] [ j ] [ 0 ] , d p [ i − 1 ] [ j ] [ 1 ] ) d p [ i ] [ j ] [ 1 ] = min ⁡ ( d p [ i ] [ j ] [ 1 ] , d p [ i − 1 ] [ j ] [ 1 ] + 1 ) dp[i][j][0]=\min(dp[i][j][0],dp[i-1][j][0], dp[i-1][j][1])\\ dp[i][j][1]=\min(dp[i][j][1],dp[i-1][j][1]+1) dp[i][j][0]=min(dp[i][j][0],dp[i1][j][0],dp[i1][j][1])dp[i][j][1]=min(dp[i][j][1],dp[i1][j][1]+1)
    • s [ i ] = ′ 1 ′ s[i]='1' s[i]=1
      d p [ i ] [ j ] [ 0 ] = min ⁡ ( d p [ i ] [ j ] [ 0 ] , d p [ i − 1 ] [ j ] [ 0 ] ) d p [ i ] [ j ] [ 1 ] = min ⁡ ( d p [ i ] [ j ] [ 1 ] , d p [ i − 1 ] [ j ] [ 1 ] ) dp[i][j][0]=\min(dp[i][j][0],dp[i-1][j][0])\\ dp[i][j][1]=\min(dp[i][j][1],dp[i-1][j][1]) dp[i][j][0]=min(dp[i][j][0],dp[i1][j][0])dp[i][j][1]=min(dp[i][j][1],dp[i1][j][1])
    • 同时,如果 i ≥ m i d i\geq mid imid j ≥ 0 j\geq 0 j0
      d p [ i ] [ j ] [ 1 ] = min ⁡ ( d p [ i ] [ j ] [ 1 ] , d p [ i − m i d ] [ j − 1 ] [ 0 ] + p r e [ i ] − p r e [ i − m i d ] ) dp[i][j][1]=\min(dp[i][j][1],dp[i-mid][j-1][0]+pre[i]-pre[i-mid]) dp[i][j][1]=min(dp[i][j][1],dp[imid][j1][0]+pre[i]pre[imid])
  • 其中, p r e pre pre 是 0 个数的前缀和
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;
typedef pair<int, pair<int, bool>> PIIB;
typedef pair<PII, PII> PIIII;

const int N = 1e5 + 10;
const int maxn = 1e6;
const int MAXN = 100;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, m, k; cin >> n >> m >> k;
	string s; cin >> s;
	s = " " + s;
	vector<int> pre(n + 1);
	for (int i = 1; i <= n; i ++ ) pre[i] = pre[i - 1] + (s[i] == '0');
	vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(k + 1, vector<int>(2, INF)));
	auto check = [&](int x)
	{
		for (int i = 0; i <= n; i ++ )
		{
			for (int j = 0; j <= k; j ++ )
			{
				dp[i][j][0] = dp[i][j][1] = INF;
			}
		}
		dp[0][0][0] = dp[0][0][1] = 0;
		for (int i = 1; i <= n; i ++ )
		{
			for (int j = 0; j <= k; j ++ )
			{
				if (s[i] == '0')
				{
					dp[i][j][0] = min({dp[i][j][0], dp[i - 1][j][0], dp[i - 1][j][1]});
					dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][1] + 1);
				}
				else
				{
					dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0]);
					dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][1]);
				}
				if (i >= x && j >= 1)
				{
					dp[i][j][1] = min(dp[i][j][1], dp[i - x][j - 1][0] + pre[i] - pre[i - x]);
				}
			}
		}
		return (min(dp[n][k][0], dp[n][k][1]) <= m);
	};
	int l = 0, r = n;
	while (l < r)
	{
		int mid = l + r + 1 >> 1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	if (r > 0) cout << r << '\n';
	else cout << -1 << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

J. Takeout Delivering(dijkstra+思维)

  • 枚举每一条边作为最终答案的边,然后看它的两个端点到 1 和 n 的距离更新最终答案
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e3 + 10;
const int maxn = 1e6;
const int MAXN = 100;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

struct edge {
	int a, b, c;
};

void solve()
{
	int n, m; cin >> n >> m;
	vector<vector<PII>> g(n + 1);
	vector<struct edge> e(m);
	for (int i = 0; i < m; i ++ )
	{
		int a, b, c; cin >> a >> b >> c;
		e[i] = {a, b, c};
		
		g[a].push_back({b, c});
		g[b].push_back({a, c});
	}
	vector<int> dist1(n + 1, INF), dist2(n + 1, INF);
	auto dijkstra = [&](int rt, vector<int>&dist)
	{
		vector<bool> st(n + 1);
		priority_queue<PII, vector<PII>, greater<PII>> pq;
		pq.push({0, rt});
		dist[rt] = 0;
		while (pq.size())
		{
			auto t = pq.top();
			pq.pop();

			int v = t.second, maxx = t.first;

			if (st[v]) continue;
			st[v] = true;

			for (int i = 0; i < g[v].size(); i ++ )
			{
				int j = g[v][i].first, w = g[v][i].second;
				if (st[j]) continue;
				if (dist[j] > max(dist[v], w))
				{
					dist[j] = max(dist[v], w);
					pq.push({dist[j], j});
				}
			}
		}
	};
	dijkstra(1, dist1);
	dijkstra(n, dist2);
	int ans = INF;
	for (int i = 0; i < m; i ++ )
	{
		auto [a, b, c] = e[i];
		int tmp = min(max(dist1[a], dist2[b]), max(dist1[b], dist2[a]));
		if (tmp <= c) ans = min(ans, tmp + c);
	}
	
	cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值