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)

对不起,大佬讲过的没做出来

image

正题开始。

我们显然可以得出 \(dp\) 式子:

\[dp_x=min_{y \in subtree} ( dp_y + a_x * b_y ) \]

然后用李超线段树一维护就好了。

代码:

$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是这个世界上最美好、最忠诚的呀~~

最爱我的小狗啦~~

image

歌词

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

posted @ 2025-10-26 22:04  晏清玖安  阅读(82)  评论(5)    收藏  举报