2026 CSP-S模拟13

T1 完全平方数

题面

完全平方数的定义:对于一个非负整数 \(x\),若存在一个非负整数 \(y\),使得 \(x=y^{2}\)
,则称 \(x\) 是完全平方数。

\(A\)有一天拿到了 \(n\) 张数字卡片,这 \(n\) 张数字卡片上分别写着 \(1\sim n\)\(n\) 个不同的数字。然后小\(A\)将这 \(n\) 张数字卡片按照数字从小到大从左到右依次摆放。我们记 \(f(l,r)\) 表示第 \(l\) 张牌到第 \(r\) 张牌上的数字中完全平方数的个数,现在让你求出对于所有的区间 \([l,r]\)\(l\) 可以等于 \(r\)),所对应的 \(f(l,r)\) 的和是多少?

输出答案对 \(998244353\) 取余的结果

解题思路

枚举每个平方数\(x\),考虑每个平方数带来的贡献,我们枚举区间长度\([1,n]\),设\(p1=\min(x,n-x+1),p2=max(x,n-x+1)\),我们尝试去滑动这个区间,当\(len<=p1\)时,\(x\)对于\(len\)长度的区间就能贡献\(len\)次,当\(p1<=len<=p2\)时,能贡献\(p1\)次,当\(p2<=len<=n\)时,能贡献\(n-len+1\)次,我们发现第一种和第三种情况可以等差数列求和,第二种情况乘起来就可以了,时间复杂度\(O(\sqrt{n})\),\(n<=10^{12}\)

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,ans;
int ksm(int a,int b) {
	int ans=1;
	for(;b;a=a*a%mod,b>>=1) {
		if(b&1) ans=ans*a%mod;
	}
	return ans;
}
signed main() {
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	freopen("square.in","r",stdin);
	freopen("square.out","w",stdout);
	cin>>n;
	int inv=ksm(2,mod-2);
	for(int i=1;i*i<=n;i++) {
		int x=i*i;
		int p1=min(n-x+1,x)%mod,p2=max(n-x+1,x)%mod;
		ans=(ans+(1+p1)%mod*p1%mod*inv)%mod;
		ans=(ans+((p2-p1)%mod+mod)%mod*p1%mod)%mod;
		p2=((n-p2)%mod+mod)%mod;
		ans=(ans+(1+p2)%mod*p2%mod*inv%mod)%mod;
	}
	cout<<ans;
	return 0;
}

T2 星际跃迁

题面

给定一个\(X\),构造出一张有向图,使得其满足以下条件:

  1. 每个点只能和比它编号的点连边
  2. 1到n的路径共有\(X\)

数据范围与评分细则

1.对于全部测试点,\(1<=X<=10^{18}\)
2.每个测试点必须\(n<=62\)才能拿到满分

解题思路

注意到测试点的特殊性质有\(2\)的整次幂,我们发现,随便取\(x\)个点,它们之间把能连边的进行连边,最后路径条数为\(2^{x-2}\),而将\(10^{18}\)二进制拆分,最高位不超过\(59\),再将\(2\sim 62\)进行连边,刚好最大构成\(2^{59}\),最后我们把\(1\)\(X\)对应的二进制位为\(1\)的点进行连边就好啦

code

#include<bits/stdc++.h>
#define fi first
#define se second
#define int long long
using namespace std;
int x;
vector<pair<int,int>> ans;
signed main() {
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	freopen("wander.in","r",stdin);
	freopen("wander.out","w",stdout);
	cin>>x;
	cout<<62<<" ";
	for(int i=2;i<=62;i++) 
	for(int j=i+1;j<=62;j++) 
	ans.push_back({i,j});
	for(int i=0;i<60;i++) 
	if((x>>i)&1) ans.push_back({1,62-1-i});
	cout<<ans.size()<<"\n";
	for(auto as:ans) cout<<as.fi<<" "<<as.se<<"\n";
	return 0;
}

T3 Delta的疑惑

题面

太长了自己看吧......

解题思路

我们发现在一个大小大于\(3\)的环中如果各个点没有两两连边,那么就有可能出现断层的情况,因此,我们找出这张图的点双,判断这些点双中的点有没有两两连边就可以了
具体的,找出每个点双及其大小\(size\),那么其中每个点应该连的边就应该是\(size-1\),我们记录每个点的度,最后判一下就好啦,复杂度是线性的

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,m,stk[N],top,num,dfn[N],low[N],du[N];
vector<int>e[N];
vector<vector<int>>dcc;
bool vis[N];
void tj(int x) {
	dfn[x]=low[x]=++num;
	stk[++top]=x;
	for(int y:e[x]) {
		if(dfn[y]) {
			low[x]=min(low[x],dfn[y]);
			continue;
		}
		tj(y);
		low[x]=min(low[x],low[y]);
		if(dfn[x]==low[y]) {
			vector<int>res;
			while(stk[top]!=y) res.push_back(stk[top--]);
			res.push_back(y),top--;
			res.push_back(x);
			dcc.push_back(res);
		}
	}
}
signed main() {
	freopen("delta.in","r",stdin);
	freopen("delta.out","w",stdout);
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1,x,y;i<=m;i++) {
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
		du[x]++,du[y]++;
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) top=0,tj(i);
	for(int i=0;i<dcc.size();i++) 
	for(int j=0;j<dcc[i].size();j++) 
	du[dcc[i][j]]-=dcc[i].size()-1;
	for(int i=1;i<=n;i++) if(du[i]!=0) return (cout<<"WA\n"),0;
	cout<<"AC\n";
	return 0;
}

T4 魔法相机

题面

小 M 和小 S 在一起看云。有 \(n\) 朵云飘在空中,高度互不相同。魔法少女小 S 施展魔法,将这 \(n\) 朵云变成了 \(n\) 种不同的颜色。

云可以看成数轴上连续的一段,初始时第 ii 朵云占据数轴上 \([l_i,r_i]\)这段区间。云会以每秒 \(1\) 个单位的速度向负方向移动,因此第 \(t\) 秒时第 \(i\) 朵云占据区间 \([l_i − t, r_i − t]\)。小 M 和小 S 位于数轴上的 \(0\) 点处。

小 M 有一个魔法相机。我们知道,利用普通相机拍摄云层的时候,离地面更低的云可能会遮挡更高的云。但使用魔法相机可以拍摄下所有的云!具体来说,如果小 M 在第 \(t\) 秒使用魔法相机,那么小 M 可以拍下所有在第 \(t\) 秒时覆盖了 \(0\) 这个点的云。

魔法相机有一个冷却时间 \(s\)。使用魔法相机后,下次使用至少要过 \(s\) 秒,即:在第 \(t\) 秒使用魔法相机后,只能在第 \(t + s\) 秒及以后再次使用。

小 M 想要记录下尽可能多的颜色,于是她很好奇,在合理安排使用魔法相机的时间的情况下,最多能拍下多少朵不同的云呢?因为小 M 非常可爱,所以您需要帮助她。

解题思路

首先可以看出来这是一个很显然的\(dp\),云往左走等价于人往右走,间隔\(s\)秒等价于距离不小于\(s\),设\(dp_i\)为坐标为\(i\)且在\(i\)处拍照的最大值,可以得出状态转移方程

\[dp_i=\max\{dp_j+sum_{j<l<=i,r>=i}\} \]

我们用扫描线优化一下,再用线段树维护\(dp\)值,将云朵信息储存在右端点处,每次在线段树上查询最值并放到线段树\(i\)对应的节点上,若扫描到了右端点,则将位于\([l,r]\)\(dp\)值加\(1\),最后直接查询根节点的\(max\),就得到了一份时间复杂度\(O(VlogV)\),空间复杂度\(O(V)\)的代码

#include<bits/stdc++.h>
#define int long long
#define lson tr[rt].ls
#define rson tr[rt].rs
using namespace std;
const int N=2e5+10;
int n,s,v,dp[N],root,tot;
vector<int>b[N];
struct SegmentTree {int ls,rs,mx,tag;} tr[N*50];
void pushup(int rt) {tr[rt].mx=max(tr[lson].mx,tr[rson].mx);}
void pushdown(int rt) {
	if(!tr[rt].tag) return;
	int tag=tr[rt].tag;
	tr[rt].tag=0;
	if(!lson) lson=++tot;
	if(!rson) rson=++tot;
	tr[lson].mx+=tag,tr[rson].mx+=tag;
	tr[lson].tag+=tag,tr[rson].tag+=tag;
}
void update(int &rt,int l,int r,int L,int R,int val) {
	if(!rt) rt=++tot;
	if(L<=l&&r<=R) return tr[rt].mx+=val,tr[rt].tag+=val,void();
	int mid=(l+r)>>1;
	pushdown(rt);
	if(L<=mid) update(lson,l,mid,L,R,val);
	if(R>mid) update(rson,mid+1,r,L,R,val);
	pushup(rt);
} 
int query(int rt,int l,int r,int L,int R) {
	if(!rt||L>R) return 0;
	if(L<=l&&r<=R) return tr[rt].mx;
	int mid=(l+r)>>1,ans=0;
	pushdown(rt);
	if(L<=mid) ans=max(ans,query(lson,l,mid,L,R));
	if(R>mid) ans=max(ans,query(rson,mid+1,r,L,R));
	return ans;
}
signed main() {
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	freopen("camera.in","r",stdin);
	freopen("camera.out","w",stdout);
	cin>>n>>s;
	for(int i=1,l,r;i<=n;i++) {
		cin>>l>>r;
		if(r>=0)
		b[r].push_back(l),v=max(v,r);
	}
	for(int i=0;i<=v;i++) {
		dp[i]=query(root,0,v,0,i-s);
		update(root,0,v,i,i,dp[i]);
		for(int l:b[i]) update(root,0,v,max(0ll,l),i,1);
	}
	cout<<tr[root].mx<<"\n";
	return 0;
}

但是\(V\)\(10^{9}\)的,这么交上去就炸了,我们考虑怎么去优化这个转移。
注意到当我们不断转移时有很多节点的值是一样的,因为答案最大不超过\(n\),并且在不添加新的云朵时,这个值是单调的,所以我们考虑按右端点将云朵排序,再枚举这个云朵,一段一段地往这个线段树里面填数就好啦
具体的,我们定义下一个要填数的位置为\(pos\),一开始\(pos=r_{last}+1\),取\(max1=\max_{0<=j<=pos-s}\{dp_j\}\),\(dp_k=max2\)为之后第一个大于\(max1\)的节点(这个树上二分就能求),若不存在\(max2\),则\(pos\sim r\)均可以填\(max1\),否则,在\(pos\sim min(r,k-1)\)填入\(max1\),再让\(pos=k\),不断进行此操作,直至把\(r\)填完为止。

复杂度证明

注意到操作次数决定于第二类操作的次数,首先这个\(max1\)是不严格递增的,这个若我们的区间覆盖了这个\(max2\),之后的\(max1\)就会增加,\(max1\)最多增加\(n\)次,即这个操作最多执行\(n\)次,若没有覆盖下一个\(max2\),那么这个在第\(i\)朵云处的操作就会终止,所以操作次数均摊是\(O(n)\)
总时间复杂度\(O(nlogV)\)

code

#include<bits/stdc++.h>
#define lson tr[rt].ls
#define rson tr[rt].rs
#define fi first
#define se second
using namespace std;
const int N=2e5+10;
int n,s,v,dp[N],root,tot,cnt;
struct node {int l,r;}q[N];
struct SegmentTree {int ls,rs,mx,tag;} tr[N*50];
void pushup(int rt) {tr[rt].mx=max(tr[lson].mx,tr[rson].mx);}
void pushdown(int rt) {
	if(!tr[rt].tag) return;
	int tag=tr[rt].tag;
	tr[rt].tag=0;
	if(!lson) lson=++tot;
	if(!rson) rson=++tot;
	tr[lson].mx+=tag,tr[rson].mx+=tag;
	tr[lson].tag+=tag,tr[rson].tag+=tag;
}
void update(int &rt,int l,int r,int L,int R,int val) {
	if(!rt) rt=++tot;
	if(L<=l&&r<=R) return tr[rt].mx+=val,tr[rt].tag+=val,void();
	int mid=(l+r)>>1;
	pushdown(rt);
	if(L<=mid) update(lson,l,mid,L,R,val);
	if(R>mid) update(rson,mid+1,r,L,R,val);
	pushup(rt);
} 
int query(int rt,int l,int r,int L,int R) {
	if(!rt||L>R) return 0;
	if(L<=l&&r<=R) return tr[rt].mx;
	int mid=(l+r)>>1,ans=0;
	pushdown(rt);
	if(L<=mid) ans=max(ans,query(lson,l,mid,L,R));
	if(R>mid) ans=max(ans,query(rson,mid+1,r,L,R));
	return ans;
}
pair<int,int> ask(int rt,int l,int r,int val) {
	if(!rt) return {0,0};
	if(l==r) return {l,tr[rt].mx};
	int mid=(l+r)>>1;
	pushdown(rt);
	if(tr[lson].mx>val) return ask(lson,l,mid,val);
	return ask(rson,mid+1,r,val);
}
signed main() {
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	freopen("camera.in","r",stdin);
	freopen("camera.out","w",stdout);
	cin>>n>>s;
	for(int i=1,l,r;i<=n;i++) {
		cin>>l>>r;
		if(r>=0) q[++cnt]={l,r},v=max(v,r);
	}
	sort(q+1,q+1+cnt,[](node a,node b) {return a.r<b.r;});
	q[0].r=-1;
	for(int i=1;i<=cnt;i++) {
		int l=q[i].l,r=q[i].r,last=q[i-1].r,pos=last+1;
		while(1) {
			int mx=query(root,0,v,0,pos-s);
			auto to=ask(root,0,v,mx);
			if(to.se==0||to.fi+s>r) {
				update(root,0,v,pos,r,mx);
				break;
			} 
			update(root,0,v,pos,to.fi+s-1,mx);
			pos=to.fi+s;
		}
		update(root,0,v,max(0,l),r,1);
	}
	cout<<tr[root].mx;
	return 0;
}
posted @ 2026-08-16 17:56  技技技技技  阅读(0)  评论(0)    收藏  举报