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好题:

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

-
1.这种树的树高是 \(\log\) 级别的。
-
2.在 \(4\times 10^4\) 下素数有 \(4\times 10^3\),这么多个。
-
3.vector跑的真的很快。
t5 trip

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

浙公网安备 33010602011771号