联盟场-提高组-R7 & ZYZSC-S-Round 11

LGP17194 KOI 2026 #2 分发零食

原题链接:KOI 2026 #2 分发零食

分析

和 这个 有点像,不过不太一样。

其实抽象一下就是拓扑排序,但是,场上搓出来的东西......也类似吧......就是这个是 deg[i]=1 的时候出列......

获得了 97 p t s 97pts 97pts。继续努力吧。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int n;
set<int> pos[N];
bool in_ans[N]; //id
bool vis[N]; //snack
vector<int> ans;
struct node{
	int id;
};
int c[N];
bool operator<(const node &tmp1, const node &tmp2){
	return c[tmp1.id] < c[tmp2.id];
}
priority_queue<node> q;
vector<int> a[N];
signed main(){
	freopen("snack.in", "r", stdin);
	freopen("snack.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++){
		cin >> c[i];
		for (int j = 1, aij; j <= c[i]; j++){
			cin >> aij;
			a[aij].push_back(i);
			pos[i].insert(aij);
		}
		if (c[i] == 1){
			q.push({i});
		}
	}
	if (q.empty()){
		cout << -1;
		return 0;
	}
	while (!q.empty()){
		int cur = q.top().id;
		q.pop();
		if (in_ans[cur])
			continue;
		if (c[cur] != 1){
			cout << -1;
			return 0;
		}
		ans.push_back(cur);
		in_ans[cur] = true;
		for (auto v : pos[cur]){
			for (auto vv : a[v]){
				if (in_ans[vv]) 
                    continue;
				pos[vv].erase(v);
				c[vv]--;
				if (c[vv] == 0){ 
					cout << -1;
					return 0;
				}
				if (c[vv] == 1){
					q.push({vv});
				}
			}
			break;
		}
	}
	if (ans.size() != n){
		cout << -1;
		return 0;
	}
	for (auto v : ans){
		cout << v << " ";
	}
}

CF2205E Simons and Dividing the Rhythm

原题链接:E. Simons and Dividing the Rhythm

分析

我场上这么烫吗?

需要改一个习惯啊,就是,当我们一次性没有通过 T 1 T1 T1,不要惊慌,特别是,你都过了 T 1 T1 T1 了,还搁这烫?


我们直接去 T T T 上做。不是哥们,对于小数据,你真的不会写吗?就是这个位置要不要断开?然后,你把这个直接扔到 set 里,我还不信过不了 5 5 5 分......那你 r a t i n g rating rating 不就上 1700 1700 1700 了?哎,不过后悔没有意义。

我们来尝试把这个东西设出来,然后转移,然后......


我们设 d p i , j dp_{i,j} dpi,j 表示考虑了 T T T 的前 i i i 位,分成了 j j j 段的对应的 S S S 的数量。

转移的话,肯定是从 d p i ′ , j − 1 dp_{i',j-1} dpi′,j−1,其中 i ′ < i i' < i i′<i。

值怎么搞呢......


思考一下,我们需要找的就是 border ⁡ \operatorname{border} border,这个东西,然后,我们设 d p i dp_{i} dpi 表示这一段以 i i i 结尾。转移需要找那些 j , i j,i j,i 没有 border ⁡ \operatorname{border} border 的,这样不至于算重。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
const int N = 8005;
int T;
int n, t[N];
int dp[N];
int border[N][N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> T;
	for (int cs = 1; cs <= T; cs++){
		cin >> n;
		for (int i = 1; i <= n; i++){
			cin >> t[i];
			dp[i] = 0;
		}
		for (int i = 1; i <= n; i++){
			border[i][i] = 0;
			for (int j = i + 1, cur; j <= n; j++){
				cur = border[i][j - 1];
				while (cur && t[cur + 1] != t[j]){
					cur = border[i][cur];
				}
				if (cur){
					border[i][j] = cur + 1;
				}
				else{
					if (t[i] == t[j])
						border[i][j] = i;
					else
						border[i][j] = 0;
				}
			}
		}
		dp[0] = 1;
		for (int i = 1; i <= n; i++){
			for (int j = 1; j <= i; j++){
                if (!border[j][i])
    				dp[i] = (dp[i] + dp[j - 1]) % mod;
			}
		}
		cout << dp[n] << '\n';
	}
} 

LGP6847 CEOI 2019 Magic Tree

原题链接:CEOI 2019 Magic Tree

分析

看一个启发式合并的题解吧......

首先,我们要了解一下树上启发式合并......按照 p y d pyd pyd 之前的意思,应该就是按照自己想要的直接合并......说得有点笼统。其实理解的很到位。


对,暴力的 DP 是想到了的......

就是设 d p u , t dp_{u,t} dpu,t 表示 t t t 时刻,断开 u u u 和 f a u fa_u fau 连边的最大值。

转移我也想到了,

d p u , t = v a l u , t + ∑ v ∈ s o n u d p v , t dp_{u,t}=val_{u,t}+\sum\limits_{v\in son_u}{dp_{v,t}} dpu,t=valu,t+v∈sonu∑dpv,t

其中,

v a l u , t = w u × d u ≤ t val_{u,t}=w_u\times d_u\\le t valu,t=wu×du≤t

d p u dp_u dpu 显然单调不降(个人认为如果真的可以从某些角度套用 slope trick,那么,肯定可以反悔贪心)。

可以使用 map 维护拐点,然后可以合并 map,然后,还有就是说 map 还需要维护差分,这个是会超时的,所以,我们可以直接 dsu on tree。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n, m, k;
int p[N];
int d[N], w[N];
map<int, int> dp[N];
int fa[N];
void merge(int &a, int b){
	if (dp[a].size() < dp[b].size())
		swap(a, b);
	for (auto it = dp[b].begin(); it != dp[b].end(); it++){
		dp[a][it -> first] += it -> second;
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> k;
	for (int i = 2; i <= n; i++){
		cin >> p[i];
	}
	for (int i = 1, v; i <= m; i++){
		cin >> v;
		cin >> d[v] >> w[v];
	}
	for (int i = 1; i <= n; i++){
		fa[i] = i;
	}
	for (int i = n; i >= 1; i--){
		dp[fa[i]][d[i]] += w[i];
		for (auto it = dp[fa[i]].upper_bound(d[i]); it != dp[fa[i]].end(); ){
		    if (it -> second > w[i]){
		        it -> second -= w[i];
		        break;
		    }
		    w[i] -= it -> second;
		    auto tmp = it;
		    it++;
		    dp[fa[i]].erase(tmp);
		}
		merge(fa[p[i]], fa[i]);
	}
	int ans = 0;
	for (auto it = dp[fa[1]].begin(); it != dp[fa[1]].end(); it++){
		ans += it -> second;
	}
	cout << ans;
}

LGP9522 JOIST 2022 错误拼写 / Misspelling

原题链接:JOIST 2022 错误拼写 / Misspelling

分析

场上觉得是什么差分约束,但是,不会做,看看题解吧。

不是哥们,这么自然吗?我感觉我场上应该写出来 O ( n 2 ) O(n^2) O(n2) 做法的😭

那不是可以获得将近 60 p t s 60pts 60pts!


场上我们能想到了,对于两个字典序不同,假设题目给出了 T a ≤ T b T_a\le T_b Ta≤Tb,那么,必有 i ∈ a , b − 1 i\ina,b-1 i∈a,b−1,满足第一个 S i ≠ S i + 1 S_i\neq S_{i+1} Si=Si+1,使得 S i > S i + 1 S_i>S_{i+1} Si>Si+1,记为限制一; T a > T b T_a>T_b Ta>Tb 同理,即为限制二。

设 d p i , j dp_{i,j} dpi,j 表示 i i i 是一个段的开头,填字符 j j j 的方案数,其中, i − 1 i-1 i−1 与 i i i 填写的不同。

如果从 d p k , j ′ dp_{k,j'} dpk,j′ 进行转移,需要分讨:

  • 若 j ′ > j j'>j j′>j,则不存在 k ≤ l < i , i − 1 ≤ r k\le l<i,i-1\le r k≤l<i,i−1≤r 的限制二
  • 若 j ′ < j j'<j j′<j,则不存在 k ≤ l < i , i − 1 ≤ r k\le l<i,i-1\le r k≤l<i,i−1≤r 的限制一

由于 k 是一段后缀,可以使用 set 维护。

然后,使用前缀和优化 DP,可以做到 O ( V n ) O(Vn) O(Vn),其中, V = 26 V=26 V=26。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 500005, V = 26;
int n, m;
int a[N], b[N];
int dp[N][V];
vector<int> ins1[N], ins2[N], era1[N], era2[N];
multiset<int> l, r;
int sum[N][V];
int f[N];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= m; i++){
        cin >> a[i] >> b[i];
        if (a[i] < b[i]){
            ins1[a[i]].push_back(i);
            era1[b[i] - 1].push_back(i);
        }
        if (a[i] > b[i]){
            ins2[b[i]].push_back(i);
            era2[a[i] - 1].push_back(i);
        }
    }
    for (int i = 1; i <= (int)ins1[1].size(); i++){
        l.insert(1);
    }
    for (int i = 1; i <= (int)ins2[1].size(); i++){
        r.insert(1);
    }
    for (int i = 0; i < V; i++)
        dp[1][i] = sum[1][i] = 1;
    for (int i = 2; i <= n; i++){
        int tmp;
        if (r.empty()){
            tmp = 1;
        }
        else{
            tmp = *--r.end() + 1;
        }
        int tot = 0;
        for (int j = 0; j < V; j++){
            f[j] = (sum[i - 1][j] - sum[tmp - 1][j]) % mod;
        }
        for (int j = V - 1; j >= 0; j--){
            dp[i][j] = (dp[i][j] + tot) % mod;
            tot = (tot + f[j]) % mod;
        }
        if (l.empty()){
            tmp = 1;
        }
        else{
            tmp = *--l.end() + 1;
        }
        tot = 0;
        for (int j = 0; j < V; j++){
            f[j] = (sum[i - 1][j] - sum[tmp - 1][j]) % mod;
        }
        for (int j = 0; j < V; j++){
            dp[i][j] = (dp[i][j] + tot) % mod;
            tot = (tot + f[j]) % mod;
        }
        for (int j = 0; j < V; j++){
            sum[i][j] = (sum[i - 1][j] + dp[i][j]) % mod;
        }
        for (auto v : era1[i - 1]){
            l.erase(l.find(a[v]));
        }
        for (auto v : era2[i - 1]){
            r.erase(r.find(b[v]));
        }
        for (int j = 1; j <= (int)ins1[i].size(); j++){
            l.insert(i);
        }
        for (int j = 1; j <= (int)ins2[i].size(); j++){
            r.insert(i);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++){
        for (int j = 0; j < 26; j++){
            ans = (ans + dp[i][j]) % mod;
        }
    }
    cout << (ans + mod) % mod;
}
相关推荐
程序喵大人1 小时前
【C++入门】编译链接模型 - 07 链接器怎样把符号对上号
开发语言·c++·编译器·编译链接·链接器
啵啵啵鱼1 小时前
第二章 线性表
java·数据结构·算法
虾..1 小时前
计算机图形学:几何表示
算法
局部有解1 小时前
Hot100 - 三数之和
数据结构·c++·算法
longlongzihan2 小时前
C++ 常用数据结构速查:vector、stack、queue、list、树、图、哈希表等声明与核心操作
数据结构·c++·list
思茂信息2 小时前
CST技术案例:加载正弦曲线的平面螺旋天线仿真研究
算法·平面·emc·cst·电磁仿真·仿真辐射
嫂子开门我是_我哥2 小时前
敏感性分析
算法·机器学习·论文笔记
别动我齐刘海2 小时前
实时控制学习2
linux·c++·人工智能·学习·算法
有Li2 小时前
EEG脑电图数据处理基础
人工智能·算法·机器学习·语言模型·医学生