CSP-S训练赛(2025.9.4)

t1 骑行

运气比较好,想到了 kruskal重构树,然后就想到了从小到大枚举点,然后计算贡献,这下就简单了(主要是做过类似的题 也是先对点排序然后计算相邻点贡献,那道题是对联通快做主席树然后线段树合并做这道题)。

t2 有向图删点

比较巧妙,我们把所有点拍到序列上,一个点会被计入贡献当且仅当其所有的祖先都在他的后面,那我们也可以算出来每个点被计入答案的概率就是祖先个数的倒数,我们记作 \(p_i\)。而答案就是 \(\sum p_i\)

我们发现计算祖先可以用传递闭包来算 \(O(\large\frac{n^3}{w})\)

code:

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=1e3+10,mod=998244353;
int n,ans,ni[N];
bitset<N> T[N];
signed main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	cin>>n;ni[1]=1;
	for(int i=2;i<N;i++) ni[i]=(-mod/i*ni[mod%i]%mod+mod)%mod;
	for(int i=1;i<=n;i++){
		string s;cin>>s;
		for(int j=0;j<n;j++){
			if(s[j]=='1') T[i].set(j+1);
		}
		T[i].set(i);
	}
	for(int j=1;j<=n;j++){
		for(int i=1;i<=n;i++) if(T[i][j]) T[i]|=T[j];
	}
	for(int j=1;j<=n;j++){
		int cnt=0;
		for(int i=1;i<=n;i++) if(T[i][j]) cnt++;
		ans=(ans+ni[cnt])%mod;
	}
	cout<<ans;
	return 0;
}

t3 跑步

dp好题:image
image

t4 数字
猎奇暴力,我就不讲这题的做法了,讲点性质。
image

  • 1.这种树的树高是 \(\log\) 级别的。

  • 2.在 \(4\times 10^4\) 下素数有 \(4\times 10^3\),这么多个。

  • 3.vector跑的真的很快。

t5 trip
image
很好的题,但是似乎只能用点分治,线段树分治/整体二分似乎都是不可做的。所以题出的很好,但是基本上用不到,贴一篇题解吧。
题目大意: 一棵树, 每条边有一个区间, 问有多少路径, 使得路径上所有区间的交集不 为空。

算法 1

根据题意模拟, 枚举起终点并计算交集。

时间复杂度 \(0\left(\mathrm{n}^{3}\right)\)

期望得分 16 分。

算法 2

对于同一个起点, 通过一次 dfs 即可算出所有终点的答案。

时间复杂度 \(0\left(n^{2}\right)\)

期望得分 \(32-60\) 分。

算法 3

对于 \(r \leq 10\), 可以枚举一段区间, 然后只保留包含这段区间的所有边, 形成一些连通块, 计算路径数量。容斥即可。

时间复杂度 \(0\left(r^{2} n\right)\)

结合前面, 期望得分 \(44-64\) 分。

算法 4

对于完全二叉树的测试点, 我们先求出每个点向上跳到一个祖先的位置的过程中, 区间 的交集。然后在祖先处离散化统计答案。暴力排序复杂度 \(O\left(n \log ^{2}(n)\right)\), 使用归并排序可 以做到 \(O(n \log (n))\)

结合前面, 期望得分56-68分

算法 5

对于一条链的测试点, 考虑分治, 每次在中点处统计左半边到右半边的答案。同样是暴 力排序复杂度 \(O\left(n \log ^{2}(n)\right)\), 使用归并排序可以做到 \(O(n \log (n))\)

结合前面, 期望得分 \(68-72\) 分。

算法 6

算法 5 提示了分治的做法, 不妨考虑点分治, 每次找到重心然后统计跨过重心的答案。 这个就利用 dfs, 算出每个点到重心路径上道路的区间交集。比较方便的做法是先统计 交集为空的情况数,这时只需要统计有多少交集区间的 \(\mathrm{l}\) 大另外的交集区间的 \(\mathrm{r}\) 。 做的过程中仍然要对 \(\mathrm{l}, \mathrm{r}\) 离散化, 采用归并排序, 或者离线等方法, 可以把复杂度由 \(O\left(n \log ^{2}(n)\right)\) 降为 \(O(n \log (n))\), 但常数较大。实现良好的 \(O\left(n \log ^{2}(n)\right)\) 做法应该也可以通过。 期望得分 100 分。

code:

#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
#define mkp make_pair
#define FI first
#define SE second
const int N=5e5+7;
const int INF=1e9+7;
void read(int &x){
	char c=getchar(); x=0;
	while(c<'0'||c>'9')c=getchar();
	while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar();
}
int n,num=0,cnt=0,sum,size;
ll ans=0;
struct edge{int v,next,l,r;}e[N*2];
int pos[N],sz[N],vis[N],sl[N],sr[N],px[N];
void add(int x,int y,int l,int r){e[num]=(edge){y,pos[x],l,r}; pos[x]=num++;}
void dfs0(int x,int f){
	sz[x]=1;
	repG(i,x){
		if(vis[e[i].v]||e[i].v==f)continue;
		dfs0(e[i].v,x);
		sz[x]+=sz[e[i].v];
	}
}
int fd(int x,int v){
	repG(i,x){
		if(vis[e[i].v])continue;
		if(sz[e[i].v]>sz[x])continue;
		if(sz[e[i].v]>v-sz[e[i].v])return fd(e[i].v,v);
	}
	return x;
}
vector<int>ds[N];
int z[N*2];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >Q[N];
void ps(int o,int x){
	int so=ds[o].size();
	if(!so){
		ds[o].push_back(x);
		return;
	}
	rep0(j,so){
		if(ds[o][j]==x)return;
		if(ds[o][j]>x){
			ds[o].push_back(0);
			for(int k=so;k>j;k--)ds[o][k]=ds[o][k-1];
			ds[o][j]=x;
			return;
		}
	}
	ds[o].push_back(x);
}
void dfs1(int x,int f,int o,int l,int r){
	size++;
	if(l>r)sum++;
	else{
		sl[l]++;
		sr[r]++;
	}
	repG(i,x){
		if(vis[e[i].v]<o)continue;
		if(e[i].v==f)continue;
		dfs1(e[i].v,x,o,max(l,px[e[i].l]),min(r,px[e[i].r+1]-1));
	}
}
void mer(int o1,int o2){
	int n1=0,n2=0,nw=0,s1=ds[o1].size(),s2=ds[o2].size();
	while(n1<s1&&n2<s2){
		if(ds[o1][n1]<ds[o2][n2])z[nw++]=ds[o1][n1++];
		else{
			if(ds[o1][n1]==ds[o2][n2])n1++;
			z[nw++]=ds[o2][n2++];
		}
	}
	while(n1<s1)z[nw++]=ds[o1][n1++];
	while(n2<s2)z[nw++]=ds[o2][n2++];
	rep0(j,s1)ds[o1][j]=z[j];
	REP(j,s1,nw-1)ds[o1].push_back(z[j]);
}
void solve(int x,int o){
	dfs0(x,0);
	int r=fd(x,sz[x]);
	vis[r]=o;
	repG(i,r){
		if(vis[e[i].v])continue;
		int nw=++cnt;
		solve(e[i].v,nw);
		ps(nw,e[i].l);
		ps(nw,e[i].r+1);
		int ls=ds[nw].size();
		for(int j=0;j<ds[nw].size();j++)px[ds[nw][j]]=j+1;
		rep0(j,ls+1)sl[j]=sr[j]=0;
		sum=size=0;
		dfs1(e[i].v,r,o,px[e[i].l],px[e[i].r+1]-1);
		rep(j,ls)sr[j]+=sr[j-1];
		REP(j,2,ls)ans-=1ll*sl[j]*sr[j-1];
		ans-=1ll*sum*size;
		ans+=1ll*sum*(sum+1)/2ll;
		ans+=1ll*sum;
		Q[o].push(mkp((int)ds[nw].size(),nw));
	}
	if(Q[o].empty())return;
	while(1){
		pair<int,int>t1=Q[o].top();
		Q[o].pop();
		if(Q[o].empty()){
			ds[o].clear();
			for(int j=0;j<ds[t1.SE].size();j++)ds[o].push_back(ds[t1.SE][j]);
			break;
		}
		pair<int,int>t2=Q[o].top();
		Q[o].pop();
		mer(t2.SE,t1.SE);
		Q[o].push(mkp((int)ds[t2.SE].size(),t2.SE));
	}
	int ls=ds[o].size();
	for(int j=0;j<ds[o].size();j++)px[ds[o][j]]=j+1;
	rep0(j,ls+1)sl[j]=sr[j]=0;
	sum=size=0;
	repG(i,r){
		if(vis[e[i].v]<o)continue;
		dfs1(e[i].v,r,o,px[e[i].l],px[e[i].r+1]-1);
	}
	rep(j,ls)sr[j]+=sr[j-1];
	REP(j,2,ls)ans+=1ll*sl[j]*sr[j-1];
	ans+=1ll*sum*size;
	ans-=1ll*sum*(sum+1)/2ll;
}
int main(){
	freopen("trip.in","r",stdin);
	freopen("trip.out","w",stdout);
	memset(pos,-1,sizeof(pos));
	scanf("%d",&n);
	rep(i,n-1){
		int u,v,l,r; read(u); read(v); read(l); read(r);
		add(u,v,l,r); add(v,u,l,r);
	}
	solve(1,++cnt);
	ll tot=n;
	tot=tot*(tot-1)/2ll;
	printf("%lld\n",tot-ans);
	return 0;
}
posted @ 2025-09-08 08:56  NeeDna  阅读(22)  评论(0)    收藏  举报