CSP-S模拟39&2025多校冲刺CSP模拟赛8
前言:
嘻嘻,虽迟但到(其实没啥意义)的改题
T1:最小生成树(tree)
据说养鸡大户蛙蛙说原始数据暴力可过
请看显然不是我的码风的code
#include<bits/stdc++.h>//骇死我力假了inf版线段树!!!
using namespace std;
#define lson (root << 1)
#define rson (root << 1 | 1)
const int _ = 100010;
long long ans, as[_];
int n, m, x, y;
bool v[_];
struct hhh{
long long l, r;
long long z;
}a[_];
struct rain{
int l, r;
long long mn, lazy;
}tree[_ << 3];
inline bool bbb(hhh x, hhh y){
if(x. l != y. l){
return x. l < y. l;
}
return x. r < y. r;
}
inline bool ltong(){
int r = 1;
for(int i = 1; i <= m; i ++){
while(a[i]. r <= r && i <= m){
i ++;
}
if(i == m + 1){
break;
}
if(a[i]. l > r){
return 0;
}
r = a[i]. r;
}
if(r < n){
return 0;
}
return 1;
}
int main(){
scanf("%d%d", & n, & m);
for(int i = 1; i <= m; i ++){
scanf("%lld%lld%lld", & a[i]. l, & a[i]. r, & a[i]. z);
}
sort(a + 1, a + m + 1, bbb);
if(! ltong()){
printf("-1");
return 0;
}
memset(as, 0x3f, sizeof(as));
for(int i = 1; i <= m; i ++){
for(int j = a[i]. l; j < a[i]. r; j ++){
as[j] = min(as[j], a[i]. z);
}
}
for(int i = 1; i < n ;i ++){
ans += as[i];
}
printf("%lld", ans);
return 0;
return 0;
}//🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢🤢
我们可以使用瞪眼 + 手模的方法从题中得出一个结论:没有一种方案比相邻两个点相连,最后形成的链更优
那我们需要维护的就是每段区间的最小值,并查集和线段树均可。(这里是线段树)
代码:
$code$
#include<iostream>
#include<algorithm>
#define int long long
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=1e5+5;
int m,n,l,r,w,ans;
struct css{
int l,r,val;
bool operator<(const css &fxt)const{
if(l!=fxt.l) return l<fxt.l;
else return r<fxt.r;
}
}a[N];
struct flower{
int l,r,minn,lazy;
}tr[N<<3];
inline void pushup(int id){
tr[id].minn=(tr[lid].minn+tr[rid].minn);
}
inline void pushdown(int id){
tr[lid].minn=min(tr[lid].minn,tr[id].lazy*(tr[lid].r-tr[lid].l+1));
tr[rid].minn=min(tr[rid].minn,tr[id].lazy*(tr[rid].r-tr[rid].l+1));
tr[lid].lazy=min(tr[lid].lazy,tr[id].lazy);
tr[rid].lazy=min(tr[rid].lazy,tr[id].lazy);
tr[id].lazy=1e18;
}
inline void build(int id,int l,int r){
tr[id].l=l;tr[id].r=r;
tr[id].lazy=tr[id].minn=1e18;
if(l==r) return ;
int mid=(l+r)>>1;
build(lid,l,mid);build(rid,mid+1,r);
pushup(id);
}
inline void update(int id,int l,int r,int val){
if(l<=tr[id].l&&tr[id].r<=r){
tr[id].minn=min(tr[id].minn,val*(tr[id].r-tr[id].l+1));
tr[id].lazy=min(tr[id].lazy,val);
return ;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
if(l<=mid) update(lid,l,r,val);
if(r>mid) update(rid,l,r,val);
pushup(id);
}
inline int query(int id,int l,int r){
if(tr[id].l==tr[id].r) return tr[id].minn;
pushdown(id);
int res=0,mid=(tr[id].l+tr[id].r)>>1;
if(l<=mid) res=(res+query(lid,l,r));
if(r>mid) res=(res+query(rid,l,r));
return res;
}
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
build(1,1,n);
for(int i=1;i<=m;i++) cin>>a[i].l>>a[i].r>>a[i].val;
sort(a+1,a+1+m);
for(int i=1;i<=m;i++) update(1,a[i].l,a[i].r-1,a[i].val);
int ans=query(1,1,n-1);
if(ans>=1e18) cout<<-1<<'\n';
else cout<<ans<<'\n';
return 0;
}
T2:最短路(roads)
思路:
其实正解是拆点。
把一个点拆为这个点度数+1个点,然后从下往上依次从大到小连入边,然后再从下向上找第一个大于这个点入边的出边然后连上,。并且在一个点拆出的多个点之间连上权值为 \(0\) 的边。最后跑 \(dij\) 就好了。
代码:
$code$
#include<iostream>
#include<algorithm>
#include<queue>
#define int long long
using namespace std;
const int N=7e5+5;
int n,m,x,y,a,b,cnt,tot,val[N],dfn[N],head[N],dis[N];
struct flower{
int to,a,b;
bool operator<(const flower &css)const{
return a>css.a;
}
};vector<flower> v[N];
struct wutong{
int to,nxt,val;
}e[N];
inline void add(int x,int y,int z){
e[++tot].to=y;
e[tot].val=z;
e[tot].nxt=head[x];
head[x]=tot;
}
signed main(){
freopen("roads.in","r",stdin);
freopen("roads.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x>>y>>a>>b;
v[x].push_back((flower){y,a,b});
}
for(int i=1;i<=n;i++){
dfn[i]=++cnt;
val[cnt]=1e18;
sort(v[i].begin(),v[i].end());
for(auto j:v[i]) val[++cnt]=j.a;
}
dfn[n+1]=++cnt;
for(int i=1;i<=n;i++){
int l=v[i].size();
for(int j=0;j<l;j++){
int to=lower_bound(val+dfn[v[i][j].to],val+dfn[v[i][j].to+1],v[i][j].a,greater<int>())-val-1;
add(dfn[i],to,v[i][j].a);
add(dfn[i]+j+1,to,v[i][j].a-v[i][j].b);
add(dfn[i]+j+1,dfn[i]+j,0);
}
}
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> q;
for(int i=1;i<=cnt;i++) dis[i]=1e18;
dis[1]=0;q.push({0,1});
while(!q.empty()){
auto t=q.top();q.pop();
int x=t.second;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(dis[y]>dis[x]+e[i].val){
dis[y]=dis[x]+e[i].val;
q.push({dis[y],y});
}
}
}
for(int i=1;i<=n;i++){
int ans=dis[dfn[i]];
if(ans>=1e18) cout<<-1<<' ';
else cout<<ans<<' ';
}
return 0;
}
T3:计算任务(mission)
原始数据暴力也都过了,不过赛后重测全卡了
较为简单的折半警报器。不会的可以 \(bdfs\) 一下,也可以去做一下鬼街,也可以微微看一下 this
给每个任务的警报器分摊下来,即给每一个电脑安装一个小警报器。不难发现只有至少一个电脑完成了分摊任务,即到达该小警报器的阈值,这个任务才有可能被完成。
所以操作一就是给电脑安装小警报器,操作二就是查找有没有小警报器“响”了的。有的话就查看大警报器,如果大警报器“响”了的话就代表该任务已经完成,我们就可以记录答案准备输出了。如果大报警器没“响”的话,我们就更新一下“响”了的小报警器的阈值,继续操作就好了。
代码:
$code$
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define int long long
using namespace std;
const int N=2e5+5;
struct flower{
int lim,id;
bool operator<(const flower &css)const{
return lim>css.lim;
}
};priority_queue<flower> q[N];
int m,n,op,last,y,k,p,x,cnt,a[N],lim[N];
bool st[N];
vector<int> pos[N],ans;
inline int calc(int x){
int res=0;
for(auto y:pos[x]) res+=a[y];
return res;
}
inline void insert(int x){
int t=(lim[x]-calc(x)+pos[x].size()-1)/pos[x].size();
for(auto y:pos[x]) q[y].push((flower){a[y]+t,x});
}
signed main(){
freopen("mission.in","r",stdin);
freopen("mission.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
while(m--){
cin>>op;
if(op==1){
cin>>y>>k;
y^=last;
++cnt;
while(k--){
cin>>p;
p^=last;
pos[cnt].push_back(p);
}
lim[cnt]=calc(cnt)+y;
insert(cnt);
}else{
cin>>x>>y;
x^=last;y^=last;
a[x]+=y;
while(!q[x].empty()){
auto t=q[x].top();
if(t.lim>a[x]) break;
q[x].pop();
int w=t.id;
if(st[w]) continue;
if(calc(w)>=lim[w]){
st[w]=true;
ans.push_back(w);
continue;
}
insert(w);
}
sort(ans.begin(),ans.end());
last=ans.size();
cout<<last<<' ';
for(int i=0;i<last;i++) cout<<ans[i]<<' ';
cout<<'\n';
ans.clear();
}
}
return 0;
}
T4:树上纯树(ture)
对不起,大佬讲过的原没做出来

正题开始。
我们显然可以得出 \(dp\) 式子:
然后用李超线段树一维护就好了。
代码:
$code$
#include<iostream>
#define int long long
using namespace std;
const int N=1e5+5,inf=1e18;
int n,u,v,a[N],b[N],cnt,tot,head[N],rt[N];
struct flower{
int k,b;
}line[N];
struct css{
int l,r,x;
}tr[N<<3];
struct miss{
int to,nxt;
}e[N<<1];
inline void add(int x,int y){
e[++tot].to=y;e[tot].nxt=head[x];head[x]=tot;
e[++tot].to=x;e[tot].nxt=head[y];head[y]=tot;
}
inline int calc(int id,int x){
return line[id].k*x+line[id].b;
}
inline bool cmp(int i,int j,int x){
if(calc(i,x)>calc(j,x)) return true;
else return false;
}
inline void insert(int &rt,int l,int r,int id){
if(!rt) rt=++cnt;
if(!tr[rt].x){
tr[rt].x=id;
return ;
}
int mid=(l+r)>>1;
if(calc(id,mid)<calc(tr[rt].x,mid)) swap(id,tr[rt].x);
if(calc(id,l)<calc(tr[rt].x,l)) insert(tr[rt].l,l,mid,id);
if(calc(id,r)<calc(tr[rt].x,r)) insert(tr[rt].r,mid+1,r,id);
}
inline int query(int rt,int l,int r,int x){
if(!rt) return inf;
int ans=calc(tr[rt].x,x);
if(l==r) return ans;
int mid=(l+r)>>1;
if(x<=mid) ans=min(ans,query(tr[rt].l,l,mid,x));
else ans=min(ans,query(tr[rt].r,mid+1,r,x));
return ans;
}
inline int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
if(l==r){
if(calc(tr[x].x,l)>calc(tr[y].x,l)) return y;
return x;
}
int mid=(l+r)>>1;
tr[x].l=merge(tr[x].l,tr[y].l,l,mid);
tr[x].r=merge(tr[x].r,tr[y].r,mid+1,r);
insert(x,l,r,tr[y].x);
return x;
}
inline void dfs(int x,int fa){
int f=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
f++;
dfs(y,x);
rt[x]=merge(rt[x],rt[y],-N,N);
}
line[x].k=b[x];
if(f) line[x].b=query(rt[x],-N,N,a[x]);
else line[x].b=0;
insert(rt[x],-N,N,x);
}
signed main(){
freopen("ture.in","r",stdin);
freopen("ture.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<n;i++){
cin>>u>>v;
add(u,v);
}
dfs(1,0);
for(int i=1;i<=n;i++) cout<<line[i].b<<' ';
return 0;
}
后言
🌸表白小狗
众所周知,一个回文串是一个好串
而 $ dog $ 与 $ god $ 拼一起显然是回文的
所以......
小狗是这个世界的真神
ta是这个世界上最美好、最忠诚的呀~~
最爱我的小狗啦~~

歌词
若这个世界凋谢
我会守在你身边
用沉默坚决
对抗万语千言
倘若这世间
一切都在无情的崩裂
我会用手中的线为你缝原
陪你看日升月潜
陪你看沧海变迁
陪你一字又一言
谱下回忆的诗篇
陪你将情节改写
陪你将八荒走遍
只因你读得懂我
而你注定是我的心头血
这是缘
亦是命中最美的相见
别恨天
笑容更适合你的脸
再一遍
记起从前的一滴一点
别怨我不在身边
记住我会在你的心里面
当我们命运重叠
恍然大悟才发现
原来这世间
完美可以残缺
时间不停歇
仿佛落叶飞花般无解
而你在这里就温柔了一切
陪你看梅海的月
陪你踱天宁的街
陪你把我的所念
写成最后的药笺
陪你过的那些年
终究会化作永远
记得我不曾后退
在你心上陪你每个黑夜
唇齿间
不舍的是对你的留恋
叹离别
总是在该圆满之前
我的愿
并非执手相看泪满眼
而是你一往无前
拾起曾因我而有的笑脸
若故事重演
我想我依然会用我的一切
换明天就算我不在里面
可你会明白我对你的永世不变
这是缘
亦是命中最美的相见
别恨天
笑容更适合你的脸
再一遍
记起从前的一滴一点
别怨我不在身边
记住我会在你的心里面
我会在你心间
做你心头血