K77的校园生活(2025年)
K77的校园生活
2025.9.2
cf 2030 D. QED's Favorite Permutation
思路
每个点需要交换的位置是固定不变的 只有可操作的L和R在改变 用need[i]记录每个点i是否需要与点i+1交换
初始化need当\(i\not=a[i]\) \(need[min(i,a[i])]\)到\(need[max(i,a[i])-1]\)都+1 最终need[i]!=0的就是要换的
用set记录哪些点需要交换但是换不了 在每次修改x时 只用检查x的左右两边 即O(1)处理操作
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
int n,q,a[maxn];
char s[maxn];
int pos[maxn];
bool ok(int a,int b) {
return s[a]=='R'||s[b]=='L';
}
int need[maxn];
void solve() {
cin>>n>>q;
for (int i=1;i<=n;i++) need[i]=0;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n;i++) cin>>s[i];
for (int i=1;i<=n;i++) {
if (a[i]==i) continue;
int u=i,v=a[i];
if (u>v) swap(u,v);
need[u]++,need[v]--;
}
for (int i=1;i<=n;i++) need[i]+=need[i-1];
set<int> se;
for (int i=1;i<=n;i++) {
if (need[i]&&!ok(i,i+1)) se.insert(i);
}
while (q--) {
int x;
cin>>x;
if (need[x-1]) {
if (!ok(x-1,x)) se.erase(x-1);
if (ok(x-1,x)&&s[x-1]=='L') se.insert(x-1);
}
if (need[x]) {
if (!ok(x,x+1)) se.erase(x);
if (ok(x,x+1)&&s[x+1]=='R') se.insert(x);
}
if (s[x]=='L') s[x]='R';
else s[x]='L';
cout<<(se.empty()?"Yes\n":"No\n");
}
}
int main() {
int t=1;
cin>>t;
while (t--) solve();
}
2025.9.3
cf 2005C. Lazy Narek
思路
求最值考虑贪心 但是这个题贪不了 所以dp一下
发现这个题有点像用dp[i][j]表示前i个元素的答案 并且现在的状态为j
所以用j表示当前要组成narek差了哪个字母
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
typedef pair<int,int> pii;
string t="narek";
string a[2000];
int dp[maxn][10];
int n,m;
pii ask(string s,int now) {
int ans=0;
for (int i=0;i<m;i++) {
if (s[i]==t[now]) {
now++;
if (now==5) {
ans+=10;
now=0;
}
}
if (t.find(s[i])!=string::npos) ans--;
}
return {ans,now};
}
void solve() {
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=0;i<=n;i++) {
for (int j=0;j<5;j++) dp[i][j]=-2e9;
}
dp[0][0]=0;
for (int i=1;i<=n;i++) {
for (int j=0;j<5;j++) {
dp[i][j]=max(dp[i-1][j],dp[i][j]);
pii temp=ask(a[i],j);
int now=temp.second,g=temp.first;
dp[i][now]=max(dp[i][now],dp[i-1][j]+g);
}
}
int ans=0;
for (int i=0;i<5;i++) ans=max(dp[n][i],ans);
cout<<ans<<endl;
}
int main() {
int t=1;
cin>>t;
while (t--) solve();
}
cf 1987 D. World is Mine
cf 1987 E. Wonderful Tree!
2025.9.9
cf 1969 C. Minimizing the Sum
思路
看到题目的数据范围 2e5 和 10 可以想到O(nm)的时间复杂度算法
多维dp
dp[i][j]表示到第i个数用了j次操作的最小值
在转移时 枚举k:dp[i][j]从dp[i-1-k][j-k]转移将i~i-k-1变成最小
dp[i][j]取其最小值
dp[n][m]即为答案
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
typedef long long ll;
ll a[maxn];
ll n,m;
ll dp[maxn][15];
void solve() {
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n;i++) {
for (int j=0;j<=m;j++) dp[i][j]=1e18;
}
for (int i=1;i<=n;i++) {
for (int j=0;j<=m;j++) {
dp[i][j]=dp[i-1][j]+a[i];
ll mn=a[i],sum=a[i];
for (int k=1;k<=j&&i-k>=1;k++) {
sum+=a[i-k],mn=min(mn,a[i-k]);
dp[i][j]=min(dp[i][j],dp[i-k-1][j-k]+mn*(k+1));
}
}
}
cout<<dp[n][m]<<endl;
}
int main() {
ll t=1;
cin>>t;
while (t--) solve();
}
cf 1969 D. Shop Game
思路
题意:alice是老板娘 可以在n个商品中选择进货 其中\(a_i\)是进价 \(b_i\)是售价 bob要买下整个小卖部 他可以直接免费拿k个商品 求alice的最大利润
按售价从大到小排序
枚举i作为分界线 i左边的选k个进价小的进货(用来打发bob) i右边的选不会亏的进货
用一个优先队列q维护打法bob的商品进价
q初始为前k个进价
如果当前进价比q中最大的进价小 说明这个进价在q中所以将这个进价从q中删去 将下一个进价加入q
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
typedef long long ll;
struct nd{
ll a,b;
};
nd a[maxn];
ll suf[maxn];
ll n,m;
void solve() {
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i].a;
for (int i=1;i<=n;i++) cin>>a[i].b;
priority_queue<ll> q;
sort(a+1,a+1+n,[](nd x,nd y){
return x.b>y.b;
});
suf[n+1]=0;
for (int i=n;i>=1;i--) {
suf[i]=suf[i+1]+max(a[i].b-a[i].a,0ll);
}
ll now=0;
for (int i=1;i<=m;i++) q.push(a[i].a),now+=a[i].a;
ll ans=0;
for (int i=m+1;i<=n;i++) {
ans=max(ans,suf[i]-now);
if (!q.empty()&&a[i].a<q.top()) {
now-=q.top();
now+=a[i].a;
q.pop();
q.push(a[i].a);
}
}
cout<<ans<<endl;
}
int main() {
ll t=1;
cin>>t;
while (t--) solve();
}
2025.9.22
cf 2056 D. Unique Median
思路
答案为 所有可能\(-\)不符合要求的
当子数组为奇数时一定符合要求
观察到\(a_i<=10\)所以考虑 O(\(n·a_i\))
枚举{\(x|1<=x<=10\)} x为子数组中位数 定义数组b 当\(a_i<=x\) 时 \(b_i=-1\) 反之为 \(1\)
对b进行前缀和
当区间\([l,r]\)和为0时 此区间不符合要求 即\([l,r]\)两个中位数{\(u!=v\)}
要使u!=v 得先满足[ l , r ] 之间有x
所以枚举\(r\) 当\(a_r=x\) 时 讲[l,r-1] 之间的每个前缀和出现次数+1
区间\([l,r]\) 间不符合要求的数即为\(pre_r\)的出现次数
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
const int mod=998244353;
typedef pair<int,int> pii;
typedef tuple<ll,ll,ll> tlll;
ll n;
int a[maxn];
int pre[maxn];
ll ans=0;
void k77(int x) {
for (int i=1;i<=n;i++) {
pre[i]=pre[i-1]+(a[i]<=x?-1:1);
}
map<int,int> mp;
int l=0;
for (int r=1;r<=n;r++) {
if (a[r]==x) {
while (l<r) mp[pre[l++]]++;
}
ans-=mp[pre[r]];
}
}
void solve() {
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
ans=(n+1)*n/2;
for (int i=1;i<=10;i++) {
k77(i);
}
cout<<ans<<endl;
}
int main() {
int t=1;
cin>>t;
while (t--) solve();
}
cf 2061 F1. Kevin and Binary String (Easy Version)
思路
因为是要使s和t匹配 所以从一边开始匹配
如果s和t最右边的块可以匹配 则t最右边的块中数量减去s最右边的块数量
如果不行 则操作一次并且答案加一 并记录不匹配的数量 在t最右边的块匹配完后 再添加入s最右边
如果不能刚好匹配玩t最右边的块 则输出-1 表示不可能使s和t匹配
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
const int mod=998244353;
typedef pair<int,int> pii;
typedef tuple<ll,ll,ll> tlll;
void solve() {
vector<pii> a,b;
string s,t;
cin>>s>>t;
int n=s.size();
s+='.';
t+='.';
int cnt=0;
for (int i=0;i<n;i++) {
cnt++;
if (s[i]!=s[i+1]) a.push_back({s[i],cnt}),cnt=0;
}
for (int i=0;i<n;i++) {
cnt++;
if (t[i]!=t[i+1]) b.push_back({t[i],cnt}),cnt=0;
}
int suf=0,ans=0;
while (!b.empty()) {
char c=b.back().first;
int num=b.back().second;
while (!a.empty()&&num>0) {
if (a.back().first==c) num-=a.back().second;
else suf+=a.back().second,ans++;
a.pop_back();
}
if (num!=0) {
cout<<-1<<endl;
return ;
}
if (suf) {
if (a.empty()) a.push_back({c^1,suf});
else {
if (a.back().first==(c^1)) a.back().second+=suf;
else a.push_back({c^1,suf});
}
suf=0;
}
b.pop_back();
}
cout<<ans<<endl;
}
int main() {
int t=1;
cin>>t;
while (t--) solve();
}
cf 2126 G1. Big Wins! (easy version)
思路
利用值域约束(a_i ≤100)缩小枚举范围
用辅助数组 b 将中位数条件转化为子数组和的约束
前缀最小值与后缀最大值快速判断有效子数组 避免暴力枚举
验证是否存在一个子数组 [l..r],满足 l ≤ i ≤ r 且 pre[r] - pre[l-1] ≥ 1(即满足中位数条件)。
med是该子数组的中位数:子数组中 ≥med的元素个数 ≥⌈k/2⌉(k是子数组长度),转化为前缀和条件是pre[r] - pre[l-1] ≥ 1(pre是辅助数组b的前缀和,b[i]对 ≥med的元素记为 1,<med的记为 -1)。a[i]是该子数组的最小值:子数组必须包含位置i(因为a[i]是最小值,子数组中所有元素 ≥a[i]),且a[i]是子数组中最小的元素。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
const int mod=998244353;
typedef pair<int,int> pii;
typedef tuple<ll,ll,ll> tlll;
int n,m,a[maxn],b[maxn],pre[maxn],premn[maxn],sufmx[maxn];
void solve() {
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
int ans=0;
for (int med=1;med<=100;med++) {
for (int i=1;i<=n;i++) {
if (a[i]>=med) pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1]-1;
}
for (int i=1;i<=n;i++) premn[i]=min(premn[i-1],pre[i]);
sufmx[n]=pre[n];
for (int i=n-1;i>=1;i--) sufmx[i]=max(sufmx[i+1],pre[i]);
for (int i=1;i<=n;i++) {
if (premn[i-1]<=sufmx[i]) ans=max(ans,med-a[i]);
}
}
cout<<ans<<endl;
}
int main() {
int t=1;
cin>>t;
while (t--) solve();
}
2025.9.23
cf 2049 D. Shift + Esc
思路
题目长得就是dp
观察数据范围 定义\(dp[i][j][k]\) 表示到达i行j列时i行操作了k次的最下花费
\(dp[i][j][k]\)可以从\(dp[i][j-1][k]\)直接转移
也可以从\(dp[i-1][j][l],l\in{[0,m-1]}\)的最小值转移
这样时间复杂度是\(O(n^4)\)
用一个\(ans[i][j]\)表示\(dp[i][j][k]\)的最小值
在每次计算完\(dp[i][j][k]\)时更新一下\(ans[i][j]\)
就可以把时间复杂度降到\(O(n^3)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
const int N=205;
ll n,m,p;
ll a[N][N];
ll dp[N][N][N];
//dp[i][j][k] 表示到达i行j列时i行操作了k次的最下花费
ll ans[N][N];
void solve() {
cin>>n>>m>>p;
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) cin>>a[i][j];
}
for (int i=0;i<=n;i++) {
for (int j=0;j<=m;j++) {
for (int k=0;k<=m;k++) dp[i][j][k]=2e18,ans[i][j]=2e18;
}
}
for (int k=0;k<m;k++) {
dp[1][1][k]=a[1][(1+k-1)%m+1]+p*k;
ans[1][1]=min(ans[1][1],dp[1][1][k]);
}
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) {
for (int k=0;k<m;k++) {
if (i==1&&j==1) continue;
dp[i][j][k]=min(dp[i][j-1][k]+a[i][(j+k-1)%m+1],
ans[i-1][j]+a[i][(j+k-1)%m+1]+k*p);
ans[i][j]=min(ans[i][j],dp[i][j][k]);
}
}
}
cout<<ans[n][m]<<endl;
}
int main() {
int t=1;
cin>>t;
while (t--) solve();
}
2025.9.24
最长上升子序列(O(nlogn))
思路
1 2 4
1 2 5
在这两个上升序列中第一个是更优的 因为它最后一个元素跟小 后面可以接的元素更多
所以在数组a中
长为x的上升子序列最后一个元素一定比长为x-1的上升子序列最后一个元素大
用\(dp[i]\)表示长为i的上升子序列最后一个元素最小是多少
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
int n;
int a[maxn];
int dp[maxn];
void solve() {
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
int ans=0;
for (int i=1;i<=n;i++) dp[i]=1e9;
for (int i=1;i<=n;i++) {
int p=lower_bound(dp+1,dp+1+i,a[i])-dp;
dp[p]=a[i];
ans=max(ans,p);
}
cout<<ans<<endl;a
}
int main() {
int t=1;
// cin>>t;
while (t--) solve();
}

浙公网安备 33010602011771号