CSP-S模拟41
T1:数轴取物(axis)
思路:
其实 \(O(n^3)\) 预处理出容量为 \(k\) 的背包在区间 \([i,j]\) 里最大能获得多少价值不难想到。
但是如何使用这个处理好的数据呢?(赛时两眼一黑就是暴搜)
我们再来考虑区间 \(dp\) (好像是我见过的第一道用两个 \(dp\) 的题)。
设 \(f_{i,j}\) 表示前 \(i\) 个背包处理到第 \(j\) 个物品的最大价值。不难得出转移方程
那么现在我们来烧烤一下我们的时间复杂度。
\(emmm... ~ O(n^2m)\) 的。显然会爆。
不过我们可以发现一个小性质:由于题目保证背包的容量单调不减,而且可以肯定的是,由于只有 \(n\) 件物品,所以最多只有 \(n\) 个背包有用,且背包容量增大答案一定不劣。所以 当 \(m>n\) 是时我们只需要后 \(n\) 个背包即可 。
这样我们的复杂度就将为 \(O(n^3)\) 了。
代码:
$code$
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N=205,M=1e5+5;
int m,n,r[M],dp[N][N][N],s[N],sum,f[N];
struct flower{
int val,cost;
}a[N],b[N];
signed main(){
// freopen("axis.in","r",stdin);
// freopen("axis.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i].cost>>a[i].val;
for(int i=1;i<=m;i++) cin>>r[i];
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
for(int k=1;k<=200;k++){
dp[i][j][k]=max(dp[i][j-1][k],dp[i][j][k]);
if(k>=a[j].cost) dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k-a[j].cost]+a[j].val);
}
}
}
for(int i=m-200;i<=m;i++){
for(int j=n;j>=1;j--){
for(int k=0;k<j;k++){
f[j]=max(f[j],f[k]+dp[k+1][j][r[i]]);
}
}
for(int j=1;j<=n;j++) f[j]=max(f[j],f[j-1]);
}
cout<<f[n];
return 0;
}
T2:排列变环(circle)
思路:
通过手模可得结论: 逆序对个数\(=2*(r-l+1)-num-1\)
因此我们发现,在一段区间内,我们取的数越多,逆序对就越少。
所以我们考虑如何来维护这一过程。
首先我们要执行的操作是选出一段合法的区间。
那什么样的区间是合法的呢?
如果一段区间内有整数和大于等于 \(k\) ,那么这个区间显然是合法的。
如果确定了这个区间合法,那么我们保证 \(sum>=k\) 的情况下尽可能的往里加尽可能多的数就好了。
最后用两个优先队列维护就好了。
对了,记得特判 \(0\) 和 \(1\) 的情况哦
代码:
$code$
#include<iostream>
#include<queue>
using namespace std;
const int N=1024;
int n,k,maxn=-1e9,sum,num,ans=1e9,w[N];
priority_queue<int,vector<int>,greater<int>> in;
priority_queue<int,vector<int>,less<int>> out;
int main(){
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>w[i];
maxn=max(maxn,w[i]);
if(w[i]>0) sum+=w[i];
}
if(maxn>=k){
cout<<0<<'\n';
return 0;
}
if(sum<k||k<=0){
cout<<-1<<'\n';
return 0;
}
for(int l=1;l<=n;l++){
sum=0;num=0;
while(!in.empty()) in.pop();
while(!out.empty()) out.pop();
for(int r=l;r<=n;r++){
sum+=w[r];
num++;
if(w[r]<0) in.push(w[r]);
while(!in.empty()&&sum<k){
sum-=in.top();//减绝对值大的数,减的数少,sum增加的多
out.push(in.top());
in.pop();
num--;
}//判合法性
if(sum<k) continue;
while(!out.empty()&&sum+out.top()>=k){
sum+=out.top();//加绝对值小的数,加的数多,sum减小的少
in.push(out.top());
out.pop();
num++;
}//求最大值
ans=min(ans,2*(r-l+1)-num-1);
}
}
cout<<ans<<'\n';
return 0;
}
T3:理想路径(path)
思路:
神秘 \(bitset\) 优化 \(floyed\).
首先我们用 \(bitset\) 优化 \(floyed\) 来判可达性。
因为题目要求的字典序最小,所以我们对每个点连的边进行一下排序。每次跑的时候就从前向后遍历边,能跑就跑。
最后用 \(vis\) 数组判一下是否有环就行了。
代码:
$code$
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<bitset>
using namespace std;
const int N=2500;
int n,m,q,op,x,y,s,t,k,step,last=1;
bool vis[N];
vector<int> v[N];
bitset<N> a[N];
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m>>q>>op;
for(int i=1;i<=m;i++){
cin>>x>>y;
a[x][y]=1;
v[x].push_back(y);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[j][i]){
a[j]=a[j]|a[i];
}
}
}//可达性
for(int i=1;i<=n;i++) sort(v[i].begin(),v[i].end());//字典序最小
while(q--){
if(last==-1) last=1;
cin>>s>>t>>k;
if(op){
s=((s^last)%n)+1;
t=((t^last)%n)+1;
k=((k^last)%n)+1;
}
if(!a[s][t]){
last=-1;
cout<<-1<<'\n';
continue;
}//到不了
last=-1;
step=0;
memset(vis,0,sizeof(vis));
while(1){
if(++step==k) last=s;//记录一下
if(s==t) break;//到了
if(vis[s]){//有环
last=-1;
break;
}
vis[s]=1;
bool flag=0;
for(int x:v[s]){//从小到大能跑就跑
if(a[x][t]==1||x==t){
flag=1;
s=x;
break;
}
}
if(!flag){
last=-1;
break;
}//没有能跑的了
}
cout<<last<<'\n';
}
return 0;
}
T4:最终礼物(gift)
🕊咕咕咕,放只鸽子


后言:
song1
忆当年春华与秋月
我抚琴你皎洁
阁楼边婆娑的泪眼
你转身多决绝
听暮雨潇潇眼前
你抽离了指尖
城楼下你飞鸿踏雪
背影凄切
诀别风月千万里
绕过姑苏汴京
几度红尘故里
唇边婉转的乡音
笔下离愁的诗句
祈求你入梦来相期
长风起 醉复醒 你偏不来梦里
(念白:苍天薄情)
当年跪神佛 誓言不分离
(念白:难解相思意)
长风去 相思寄 追寻你千万里
(念白:偏要教我忍痛断舍离)
神明坐高台 不见君踪影
拜天地 敬神明 难抵我 意难平
恨别离 你我却生别离
春又来 秋又去 此情成追忆
盼归期 不是你 空欢喜
诀别风月千万里
绕过姑苏汴京
几度红尘故里
唇边婉转的乡音
笔下离愁的诗句
祈求你入梦来相期
长风起 醉复醒 你偏不来梦里
(念白:苍天薄情)
当年跪神佛 誓言不分离
(念白:难解相思意)
长风去 相思寄 追寻你千万里
(念白:偏要教我忍痛断舍离)
神明坐高台 不见君踪影
拜天地 敬神明 难抵我意难平
恨别离 你我却生别离
春又来 秋又去 此情成追忆
盼归期 不是你 空欢喜
拜天地 敬神明 难抵我意难平
恨别离 你我却生别离
春又来 秋又去 此情成追忆
盼归期 不是你 空欢喜
song2
那一年你和我一样年纪
年轻得像首青涩的歌曲
但为了创造梦中那个新天地
你转身 匆匆走进风雨
我看见千万个可爱的你
不回头向硝烟深处奔去
多少个青春背影消失在夜里
换来晨曦
我仰望你看过的星空
穿过百年时空再相逢
你转过身之前的那个笑容
我都懂
我仰望你看过的星空
脚下大地已换了时空
你留在风中摇曳的那抹红
在心中 心中
举起手我说出同样誓言
黑白间你的笑容多清晰
你说你从来也不后悔把一生
奉献给 这片辽阔大地
我多想伸手紧紧拥抱你
告诉你一切都尘埃落定
百年前你梦想的那个新中国
有多美丽
我仰望你看过的星空
穿过百年时空再相逢
在此刻我们总会心灵相通
我都懂
我仰望你看过的星空
脚下大地已换了时空
你留在风中摇曳的那抹红
在心中 心中
我仰望你看过的星空
穿过百年时空再相逢
在此刻我们总会心灵相通
我都懂
我仰望你看过的星空
脚下大地已换了时空
你留在风中摇曳的那抹红
在心中 心中