2026-05-25~06-11 hetao1733837 的刷题记录
05-25
祝 xxxalq \operatorname{xxxalq} xxxalq 生日快乐🎂
LGP3075 USACO13FEB Partitioning the Farm G
原题链接:USACO13FEB Partitioning the Farm G
分析
最小化区域和最大值 让我想到了二分答案。难道是 DP?二分套 DP 似乎是不错的东西。
怎么写啊/ll
连个数据范围都没有吗?
难道是状压 DP?我觉得这个很合理啊,因为 N ≤ 15 N\le 15 N≤15。
原来是这样吗?我们一个维度 2 N − 1 2^{N-1} 2N−1 深搜,另一个维度二分答案!
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 21; // 稍微放大以匹配
int n, k, a[N][N], sum[N][N], c[N];
bool check(int row, int col, int x, int k){
int cnt = 0;
for (int i = 1; i <= row; i++){
c[i] = 0;
}
for (int i = 1; i <= col; i++){
for (int j = 1; j <= row; j++){
c[j] += sum[j][i];
}
bool flag = false;
for (int j = 1; j <= row; j++){
flag |= (c[j] > x);
}
if (flag){
cnt++;
for (int j = 1; j <= row; j++){
c[j] = sum[j][i];
if (c[j] > x)
return false;
}
}
}
return cnt <= k;
}
int calc(int row, int col, int k){
int l = 0, r = 225000;
while (l < r){
int mid = (l + r) >> 1;
if (check(row, col, mid, k))
r = mid;
else
l = mid + 1;
}
return l;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
cin >> a[i][j];
}
}
int ans = 0x3f3f3f3f;
for (int num = 0; num < (1 << (n - 1)); num++){
int cnt = 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
sum[cnt][j] += a[i][j];
}
cnt += (num >> (i - 1)) & 1;
}
if (k - (cnt - 1) >= 0){
ans = min(ans, calc(cnt, n, k - (cnt - 1)));
}
for (int i = 1; i <= cnt; i++){
for (int j = 1; j <= n; j++){
sum[i][j] = 0;
}
}
}
cout << ans << '\n';
return 0;
}
05-26
LGP3879 TJOI2010 阅读理解
原题链接:TJOI2010 阅读理解
分析
没啥的,STL🐂🍺!
正解
cpp
#include <bits/stdc++.h>
using namespace std;
int n, l, m;
map<string, set<int>> buc;
string s;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> l;
for (int j = 1; j <= l; j++){
cin >> s;
buc[s].insert(i);
}
}
cin >> m;
for (int i = 1; i <= m; i++){
cin >> s;
for (auto v : buc[s]){
cout << v << " ";
}
cout << '\n';
}
}
06-08
CF1876C Autosynthesis
分析
问号 \] \[ 问号 \] \[ 问号 \] {\[问号\]\[问号\]\[问号\]} \[问号\]\[问号\]\[问号
这是啥?我好饿,今天中午的饭真的是人类可以食用的吗?
也是采访到了学长的学习经验,缓和了和 xy 的关系好吧。但是感觉 xy 在点我啊。
哦,难道说把一个元素和那个与这个元素下标一样的连在一起?
那么,如果不连通,显然就 G 了。
那要是连通怎么办?要是隔一个选一个显然不是很好实现吧?
不连通真的会 G 吗?
从某些方面似乎不会......好久没有刷题了/ll
哦,原来是 fqh 的题吗/jy
咋是 内向基环树 ?
即,我们把所有的 i i i 指向 a i a_i ai,那么,形成内向基环树。若未圈出了 i i i,则选择他的出边,反之,不选择。
那么,对于所有未选择出边的点,他的入边最少有一条被选择;所有选择了出边的点,他所有的入边都没被选择。
所有叶子节点都要选择出边,剩下的直接做就行。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, a[N], b[N], in[N], id[N];
bool vis[N];
vector<int> buc;
queue<int> q;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
id[i] = i;
}
for (int i = 1; i <= n; i++){
cin >> a[i];
in[a[i]]++;
}
for (int i = 1; i <= n; i++){
if (!in[i]){
q.push(i);
vis[i] = true;
}
}
while (!q.empty()){
int u = q.front();
int v = a[u];
q.pop();
if (vis[v]){
continue;
}
buc.push_back(a[u]);
vis[v] = true;
in[a[v]]--;
if (!in[a[v]]){
vis[a[v]] = true;
q.push(a[v]);
}
}
for (int i = 1; i <= n; i++){
if (!vis[i]){
for (int u = i; a[u] != u && !vis[a[u]]; u = a[a[u]]){
buc.push_back(a[u]);
vis[u] = vis[a[u]] = true;
}
}
}
for (int i = 1; i <= n; i++){
if (!vis[i]){
cout << -1;
return 0;
}
}
int sum = 0;
for (auto v : buc){
b[v] = 1;
}
for (int i = 1; i <= n; i++){
sum += b[i];
}
cout << n - sum << '\n';
for (int i = 1; i <= n; i++){
if (!b[i]){
cout << a[i] << " ";
}
}
}
06-11
别给东外来的传球!!!
LGP11183 ROIR 2018 大数据处理 (Day2)
分析
有一种区间覆盖的感觉。咦,下节咋还有物理课/ll
物理课想了 10min ,觉得应该搞清楚最暴力的怎么搞。就是,怎么证明这个东西始终有解。写到这句话的时候,我已经明白了,就是说,每个数都是 1 的倍数,所以说,最暴力的情况显然是一个一个做。
那么,这个题似乎明朗了一点,但也只是一点。我想吃饭/ll
仔细思考一下。
画了一下......也就是说,我们可以对于所有偶数进行质因数分解,那么,有几个 2 ,就可以向后延伸多少......
然后呢?一个部分的拆解......这个是好做的吧......(虽然我不会)但是这是一个非常顺滑的 DP。
好吧,我决定看题解。但是,我没有看出来这里面要用什么数据结构。
这题的巧思在于,这个 2 i 2^i 2i 可以让人联想到 线段树!!!所有合法区间都对应了线段树上的点。
设 d p p o s , c dp_{pos,c} dppos,c 表示把线段树 p o s pos pos 点全部染成要求的最小的操作次数。这个题解写成这样会不会被wk爆掉?
转移有:
d p p o s , c = min { d p l s , c + d p r s , c − 1 d p l s , c + m n r s d p l r s , c + m n l s dp_{pos,c}=\min\begin{cases} dp_{ls,c}+dp_{rs,c}-1\\ dp_{ls,c}+mn_{rs}\\ dp_{lrs,c}+mn_{ls} \end{cases} dppos,c=min⎩ ⎨ ⎧dpls,c+dprs,c−1dpls,c+mnrsdplrs,c+mnls
其中, m n p o s = min d p p o s , c mn_{pos}=\min dp_{pos,c} mnpos=mindppos,c。
其实每次算就行了,可以优化一维,所以,最终的复杂度来到了 O ( n k ) O(nk) O(nk)。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
const int MAXN = 3000010;
int k, n, m;
int tot, rt, cnt;
vector<int> dp[MAXN], col[MAXN];
int mn[MAXN];
map<int, int> buc;
struct segtree{
int ls, rs, val;
}tr[MAXN];
int tmp[N];
void modify(int &p, int l, int r, int s, int t, int v){
if (!p)
p = ++tot;
if (s <= l && r <= t){
tr[p].val = v;
return ;
}
int mid = (l + r) >> 1;
if (s <= mid)
modify(tr[p].ls, l, mid, s, t, v);
if (t > mid)
modify(tr[p].rs, mid + 1, r, s, t, v);
}
void calc(int p){
int cur = ++tot;
int l, r;
dp[cur].clear();
col[cur].clear();
if (tr[p].val){
dp[cur].push_back(1);
col[cur].push_back(tr[p].val);
mn[cur] = 1;
return ;
}
mn[cur] = 0x3f3f3f3f;
calc(tr[p].ls);
l = tot;
calc(tr[p].rs);
r = tot;
for (int i = 0; i < (int)dp[l].size(); i++){
dp[cur].push_back(dp[l][i] + mn[r]);
col[cur].push_back(col[l][i]);
tmp[col[l][i]] = i + 1;
mn[cur] = min(mn[cur], dp[l][i] + mn[r]);
}
for (int i = 0; i < (int)dp[r].size(); i++){
int pos = tmp[col[r][i]] - 1;
if (pos != -1){
dp[cur][pos] = min(dp[cur][pos], dp[r][i] + min(dp[l][pos] - 1, mn[l]));
mn[cur] = min(mn[cur], dp[cur][pos]);
}
else{
dp[cur].push_back(dp[r][i] + mn[l]);
col[cur].push_back(col[r][i]);
mn[cur] = min(mn[cur], dp[r][i] + mn[l]);
}
}
for (int i = 0; i < (int)dp[l].size(); i++)
tmp[col[l][i]] = 0;
tot = cur;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> k >> n >> m;
int l = 0, r = -1;
for (int i = 1; i <= n; i++){
int c, v;
cin >> c >> v;
r = l + c - 1;
if (!buc[v])
buc[v] = ++cnt;
modify(rt, 0, (1 << k) - 1, l, r, buc[v]);
l = r + 1;
}
tot = 0;
calc(rt);
cout << mn[1];
return 0;
}