2025-11-21 hetao1733837的刷題記錄
LG14415/LOJ3004 [JOISC 2015] Inheritance
呃……居然tag是二分!我們考慮單調性在哪……發現對於一條邊,若其不為第$K$次操作的邊權(比較小),則不為所有小於$K$操作的刪邊。考慮怎麼$check$,模擬$Kruskal$的過程,通過$K$個並查集進行維護即可。這個思想是真的🐂🍺!
HE一下。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1005, M = 300005, K = 10005;
struct node{
int a, b, c, id;
}e[M];
int fa[K][N], sz[K][N], ans[M];
bool cmp(node x, node y){
return x.c > y.c;
}
int getfa(int k, int x){
return x == fa[k][x] ? x : fa[k][x] = getfa(k, fa[k][x]);
}
void merge(int k, int x, int y){
x = getfa(k, x);
y = getfa(k, y);
if (x == y)
return ;
if (sz[k][x] > sz[k][y])
swap(x, y);
fa[k][x] = y;
sz[k][y] += sz[k][x];
}
int n, m, k;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= m; i++){
cin >> e[i].a >> e[i].b >> e[i].c;
e[i].id = i;
}
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= k; j++){
fa[j][i] = i;
sz[j][i] = 1;
}
}
for (int i = 1; i <= m; i++){
int u = e[i].a, v = e[i].b, w = e[i].c, id = e[i].id;
int l = 1, r = k;
int pos = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (getfa(mid, u) != getfa(mid, v)){
pos = mid;
r = mid - 1;
}
else{
l = mid + 1;
}
}
ans[id] = pos;
if (pos)
merge(pos, u, v);
}
for (int i = 1; i <= m; i++)
cout << ans[i] << '\n';
}
LG14412/JOI3001 [JOISC 2015] AAQQZ
原題鏈接1:
原題鏈接2:
分析
根本不會,mhh推了兩篇題解,粘在這。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 3005;
int n, c, a[N], cntl[N], cntr[N], dp[N][N], ans;
int calc(int l, int r, int lim){
for (int i = 0; i <= c; i++)
cntl[i] = cntr[i] = 0;
if (l < 1 || l > n)
return 0;
cntl[a[l]]++;
int L = l, suml = 1, sumr = 0, maxn = 0, cnt = 0;
while (L > 1 && a[L - 1] >= a[L]){
cntl[a[--L]]++;
suml++;
}
int R = a[L];
L = 0;
for (int i = r; i <= n; i++){
cntr[a[i]]++;
if (a[i] < lim)
return maxn;
if (a[i] == lim)
cnt++;
else if (cntr[a[i]] > cntl[a[i]]){
while (R > a[i] && R >= 0){
suml -= cntl[R];
R--;
}
}
else{
while (L <= c && cntl[L] <= cntr[L]){
sumr += cntl[L];
L++;
}
}
int add = (L <= c) ? cntr[L] : 0;
int len = min(sumr + add, suml);
if (len + cnt == i - r + 1 && l >= len && l - len >= 0 && i + 1 <= n + 1)
len += dp[l - len][i + 1];
maxn = max(maxn, 2 * len + cnt);
}
return maxn;
}
void solve(){
for (int i = 0; i <= n + 1; i++)
for (int j = 0; j <= n + 1; j++)
dp[i][j] = 0;
for (int i = 1; i <= n; i++){
for (int j = i; j <= n; j++){
dp[i][j] = 0;
if (a[i] == a[j])
dp[i][j] = dp[i - 1][j + 1] + 1;
}
}
for (int i = 1; i <= n; i++)
ans = max(ans, 2 * dp[i][i] - 1 + calc(i - dp[i][i], i + dp[i][i], 0));
for (int i = 1; i < n; i++)
ans = max(ans, 2 * dp[i][i + 1] + calc(i - dp[i][i + 1], i + dp[i][i + 1] + 1, 0));
for (int i = 1; i <= n; i++){
int minn = 0;
for (int j = i + 1; j <= n; j++){
if (a[j] < a[i]) {
minn = a[j];
break;
}
}
ans = max(ans, calc(i, i + 1, minn));
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> c;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
solve();
for (int i = 1; i <= n; i++)
a[i] = c - a[i] + 1;
reverse(a + 1, a + n + 1);
solve();
sort(a + 1, a + n + 1);
int sum = 0;
for (int i = 1; i <= n; i++){
if (a[i] != a[i - 1]){
ans = max(ans, sum);
sum = 0;
}
sum++;
}
ans = max(ans, sum);
cout << ans;
return 0;
}
LG14413/LOJ [JOISC 2015] Card Game Is Great Fun
分析
14點38分
$DP$是一眼的吧……但是我好像並不會設計狀態和轉移,稍考一會。
14點39分
想到fqh講的一個trick,當時是矩陣長寬連邊,此題我們難道可以花色點數連邊?
那麼,我們會建成一張圖?這不是廢話嗎?咦,$V_i\ge 1$似乎可以貪心的$DP$一下?我在寫什麼?
那,建出圖,我們找最長鏈即可?這不是藍題嗎?
但是,問題在於建圖,這樣是$O(n^2)$難道要運用另一個限制條件——只取第一或第三個?何意味?
14點50分
何意味啊?居然是高維$DP$!
既然限制了只能取第一或第三,那麼,我們就把他計入狀態!
即設$f_{i,j}$表示當前序列的第一個元素位置為$i$,第三個元素位置為$j$。但是,選第三個直接轉移到$f_{i,j+1}$,但是選第一個沒法轉移,不知道$i$和$j$之後選哪個。
那麼,多加一維$k$表示前三個元素怎麼樣?也不太行,因為不知道棧頂牌的具體信息。
那麼,再加一維吧🤮,設$dp_{i,j,k,l}$表示上一次選擇了原序列的第$l$張牌。選第一張即可轉移到$f_{j,k,k+1,i}$,選第三張即可轉移到$f_{i, j, k+1, k}$。顯然,狀態是 $O(n^4)$無法滿足,/(ㄒoㄒ)/~~藍題別搞!
發現$k=max(j,l)$,那不就省略一維了嗎?
直接跑滿!
然後呢,我想説,一些DP題實際上是記憶化搜索的手筆,所以,不會DP時,可以嘗試寫暴力,然後記憶化一下,説不定有客觀的分數!
總結
限制扔進狀態,觀察數據範圍大略估計狀態維數,然後拿下!
正解
何意味,少開個long long卡我快20分鐘,居然報TLE!
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 505;
int f[N][N][N];
int c[N], a[N], v[N];
int n;
bool check(int x, int y){
if (!x || !y) //最開始和最結尾隨便選
return true;
if (c[x] == c[y] || a[x] == a[y])
return true;
return false;
}
int dfs(int i, int j, int k, int l){
if (f[i][j][l] != -1){
return f[i][j][l];
}
int ans = 0;
if (i <= n && check(l ,i)){
ans = max(ans, v[i] + dfs(j, k, k + 1, i));
}
if (k <= n && check(l ,k)){
ans = max(ans, v[k] + dfs(i, j, k + 1, k));
}
f[i][j][l] = ans;
return f[i][j][l];
}
int read(){
char c = getchar();
int x = 0, f = 1;
while (c < '0' || c > '9'){
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
x = x * 10 + (c - '0');
c = getchar();
}
return x * f;
}
signed main(){
n = read();
for (int i = 1; i <= n; i++){
c[i] = read();
a[i] = read();
v[i] = read();
}
memset(f, -1, sizeof(f));
printf("%lld", dfs(1, 2, 3, 0));
}
LG3615/LOJ2734 [JOISC 2016] Toilets
分析
記女生為+1,男生為-1,那麼,發現每個男生需要"消耗"一個女生以保證兩個廁所都不為空($2N$個人$N$個廁所,空依次就得崩)。那麼,轉化為後綴和,當後綴和小於$-1$時,就需要調整,然後喫喫吃,就做完了?何意味。
正解
#include <bits/stdc++.h>
using namespace std;
const int M = 100005;
int m;
long long n, ans = -1, k[M];
string s[M];
int main(){
cin >> n;
cin >> m;
for (int i = 1; i <= m; i++){
cin >> s[i] >> k[i];
reverse(s[i].begin(), s[i].end());
}
long long tmp = 0;
for (int i = m; i >= 1; i--){
long long sum = 0, v = 1e9;
for (int j = 0; j < s[i].length(); j++){
if (s[i][j] == 'F')
sum += 1;
else
sum += -1;
v = min(sum, v);
}
if (sum >= 0){
ans = min(ans, tmp + v);
tmp += sum * k[i];
}
else{
tmp += sum * (k[i] - 1);
ans = min(ans, tmp + v);
tmp += sum;
}
}
ans = -ans - 1LL;
if (tmp < 0)
cout << -1;
else
cout << ans;
}