【比赛记录】2025CSP-S模拟赛10

image

A. 返乡

当所有 \(a+b+c\) 都相等时,显然没有偏序。使这个和为 \(\lfloor\frac{3\times n}{2}\rfloor\) 时,显然是数量最多的。

然后实际上这就是最优构造,因为它显然无法再往里加东西了。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define mp make_pair
#define fir first
#define sec second
#define pb push_back
using namespace std;
namespace asbt{
int n;
vector<pair<pair<int,int>,int> > p;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	int sum=n*3/2;
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++){
			int k=sum-i-j;
			if(k>=0&&k<=n){
				p.pb(mp(mp(i,j),k));
			}
		}
	}
	cout<<p.size()<<"\n";
	for(auto x:p){
		cout<<x.fir.fir<<" "<<x.fir.sec<<" "<<x.sec<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

B. 连接

首先有一个性质,我们取的钢管一定至少有一端在分界线上。且如果另一端不在分界线上,总质量必然取到了 \(L\)\(R\)。原因是如果没有取到,就一定可以向大密度方向继续延伸(或将小密度钢管缩掉)。

于是我们只消分别计算这两种情况。对于只有一端是分界线的情况,我们可以枚举这个分界线,然后二分出另一端的位置。注意分界线有可能在左边或右边。对于两端都是分界线的情况,我们直接去二分答案,枚举左端点并双指针出右端点所在的区间,判断能否取到比 \(mid\) 更大的密度即可。

具体的判断,进行一个前缀长度和 \(sa\) 和前缀质量和 \(sm\),如果能更大,那么一定有:

\[\frac{sm_i-sm_{l-1}}{sl_i-sl_{l-1}}>mid\\ \Leftrightarrow sm_i-mid\times sl_i>sm_{l-1}-mid\times sl_{l-1} \]

所以用单调队列维护 \(sm_i-mid\times sl_i\) 的最大值即可。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e5+5;
int n,ll,rr,a[maxn],b[maxn],sa[maxn],sm[maxn],q[maxn];
double ans;
il void solve(){
	for(int i=1;i<=n;i++){
		sa[i]=sa[i-1]+a[i];
		sm[i]=sm[i-1]+a[i]*b[i];
	}
	for(int i=1,l,r;i<=n;i++){
//		cout<<i<<"\n";
		l=i,r=n;
		while(l<r){
			int mid=(l+r)>>1;
			if(sm[mid]-sm[i-1]<ll){
				l=mid+1;
			}
			else{
				r=mid;
			}
		}
		if(sm[l]-sm[i-1]>=ll){
//			cout<<l<<" "<<ll/(sa[l-1]-sa[i-1]+(ll-sm[l-1]+sm[i-1])*1.0/b[l])<<"\n";
			ans=max(ans,ll/(sa[l-1]-sa[i-1]+(ll-sm[l-1]+sm[i-1])*1.0/b[l]));
		}
		l=i,r=n;
		while(l<r){
			int mid=(l+r)>>1;
			if(sm[mid]-sm[i-1]<rr){
				l=mid+1;
			}
			else{
				r=mid;
			}
		}
		if(sm[l]-sm[i-1]>=rr){
//			cout<<l<<" "<<rr/(sa[l-1]-sa[i-1]+(rr-sm[l-1]+sm[i-1])*1.0/b[l])<<"\n";
			ans=max(ans,rr/(sa[l-1]-sa[i-1]+(rr-sm[l-1]+sm[i-1])*1.0/b[l]));
		}
	}
}
signed main(){
	scanf("%lld%lld%lld",&n,&ll,&rr);
	for(int i=1;i<=n;i++){
		scanf("%lld",a+i);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld",b+i);
	}
	solve();
//	cout<<ans<<"\n";
	reverse(a+1,a+n+1);
	reverse(b+1,b+n+1);
	solve();
	double L=1,R=1e6;
	while(R-L>1e-7){
		double mid=(L+R)/2;
		for(int i=1,l=1,r=0,hd=1,tl=0;i<=n;i++){
			while(r<n&&sm[r+1]-sm[i-1]<=rr){
				r++;
				while(hd<=tl&&sm[r]-sa[r]*mid>=sm[q[tl]]-sa[q[tl]]*mid){
					tl--;
				}
				q[++tl]=r;
			}
			while(l<n&&sm[l]-sm[i-1]<ll){
				l++;
				while(hd<=tl&&q[hd]<l){
					hd++;
				}
			}
			if(hd>tl||sm[l]-sm[i-1]<ll||sm[r]-sm[i-1]>rr){
				continue;
			}
			if(sm[q[hd]]-sa[q[hd]]*mid>sm[i-1]-sa[i-1]*mid){
				L=mid;
				goto togo;
			}
		}
		R=mid;
		togo:;
	}
	printf("%.8f",max(ans,L));
	return 0;
}
}
signed main(){return asbt::main();}

C. 习惯孤独

考虑第 \(i\) 次切要切掉的子树大小,为 \(a_{i-1}-a_i\)。切掉的子树就没用了,故这 \(k\) 次切割就成了相互独立的了。\(k\) 又那么小,考虑状压 DP。

\(f_{u,S}\) 表示 \(u\) 的子树,切割状态为 \(S\) 的方案数。那么用儿子更新父亲是容易的。然后还要考虑 \(u\) 本身。这时需要注意,\(u\) 的切割是必须放在 \(u\) 的子树之后的。

然后还得考虑 \(u\) 子树外的贡献,显然需要换根。但是这个形式,直接从总贡献中剔除一个点是比较困难的。因此需要对 \(u\) 的儿子做前后缀和。注意此时还得再算上 \(u\) 单独的贡献。

最后统计答案,我们要求 \(u\) 最后留下,所以不能用 \(f\) 统计答案。需要另计一个 \(g\) 来计算 \(u\) 的子树除了 \(u\) 的贡献。最后答案需要再除以 \(a_k\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=5e3+5,maxm=(1<<6)+5,mod=998244353;
typedef int DP[maxn][maxm];
int n,m,U,a[10],sz[maxn],hp[maxm];
DP f,g,pre,suf,h;
vector<int> e[maxn];
il int qpow(int x,int y=mod-2){
	int res=1;
	while(y){
		if(y&1){
			res=res*1ll*x%mod;
		}
		x=x*1ll*x%mod,y>>=1;
	}
	return res;
}
il void dfs1(int u,int fa){
	sz[u]=1,f[u][0]=1;
	int fid=-1;
	for(int i=0,v;i<e[u].size();i++){
		v=e[u][i];
		if(v==fa){
			fid=i;
			continue;
		}
		dfs1(v,u);
		sz[u]+=sz[v];
		for(int j=0;j<=U;j++){
			hp[j]=f[u][j];
			f[u][j]=0;
//			cout<<hp[j]<<" ";
		}
//		puts("");
		for(int j=0,S;j<=U;j++){
			S=U^j;
			for(int k=S;;k=(k-1)&S){
				(f[u][j|k]+=hp[j]*1ll*f[v][k]%mod)%=mod;
				if(!k){
					break;
				}
			}
		}
	}
	if(~fid){
		e[u].erase(e[u].begin()+fid);
	}
	for(int i=0;i<=U;i++){
		g[u][i]=f[u][i];
	}
	for(int i=0,sum;i<=U;i++){
		sum=0;
		for(int j=1;j<=m;j++){
			if(i>>(j-1)&1){
				sum+=a[j];
			}
		}
		sum=sz[u]-sum;
		for(int j=m;j;j--){
			if(i>>(j-1)&1){
				break;
			}
			if(sum==a[j]){
				(f[u][i|1<<(j-1)]+=g[u][i])%=mod;
			}
		}
	}
}
il void dfs2(int u){
	for(int i=0;i<=e[u].size()+1;i++){
		for(int j=0;j<=U;j++){
			pre[i][j]=suf[i][j]=0;
		}
	}
	pre[0][0]=suf[e[u].size()+1][0]=1;
	for(int i=0,v;i<e[u].size();i++){
		v=e[u][i];
		for(int j=0,S;j<=U;j++){
			S=U^j;
			for(int k=S;;k=(k-1)&S){
				(pre[i+1][j|k]+=pre[i][j]*1ll*f[v][k]%mod)%=mod;
				if(!k){
					break;
				}
			}
		}
	}
	for(int i=e[u].size(),v;i;i--){
		v=e[u][i-1];
		for(int j=0,S;j<=U;j++){
			S=U^j;
			for(int k=S;;k=(k-1)&S){
				(suf[i][j|k]+=suf[i+1][j]*1ll*f[v][k]%mod)%=mod;
				if(!k){
					break;
				}
			}
		}
	}
	for(int i=0,v;i<e[u].size();i++){
		v=e[u][i];
		for(int j=0,S;j<=U;j++){
			S=U^j;
			for(int k=S;;k=(k-1)&S){
				(h[v][j|k]+=pre[i][j]*1ll*suf[i+2][k]%mod)%=mod;
				if(!k){
					break;
				}
			}
		}
		for(int j=0;j<=U;j++){
			hp[j]=h[v][j];
			h[v][j]=0;
		}
		for(int j=0,S;j<=U;j++){
			S=U^j;
			for(int k=S;;k=(k-1)&S){
				(h[v][j|k]+=hp[j]*1ll*h[u][k]%mod)%=mod;
				if(!k){
					break;
				}
			}
		}
		for(int j=0;j<=U;j++){
			hp[j]=h[v][j];
		}
		for(int j=0,sum;j<=U;j++){
			sum=0;
			for(int k=1;k<=m;k++){
				if(j>>(k-1)&1){
					sum+=a[k];
				}
			}
			sum=n-sz[v]-sum;
			for(int k=m;k;k--){
				if(j>>(k-1)&1){
					break;
				}
				if(sum==a[k]){
					(h[v][j|1<<(k-1)]+=hp[j])%=mod;
				}
			}
		}
	}
	for(int v:e[u]){
		dfs2(v);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	cin>>m;
	U=(1<<m)-1;
	for(int i=1;i<=m;i++){
		cin>>a[i];
	}
	a[0]=n;
	for(int i=m+1;i;i--){
		a[i]=a[i-1]-a[i];
	}
	dfs1(1,0);
	h[1][0]=1;
	dfs2(1);
//	puts("666");
	int ans=0;
	for(int u=1;u<=n;u++){
//		cout<<u<<"\n";
		for(int i=0;i<=U;i++){
			hp[i]=0;
		}
		for(int i=0,S;i<=U;i++){
//			cout<<f[u][i]<<" "<<g[u][i]<<" "<<h[u][i]<<"\n";
			S=U^i;
			for(int j=S;;j=(j-1)&S){
				(hp[i|j]+=g[u][i]*1ll*h[u][j]%mod)%=mod;
				if(!j){
					break;
				}
			}
		}
		(ans+=hp[U])%=mod;
	}
	cout<<ans*1ll*qpow(a[m+1])%mod;
	return 0;
}
}
int main(){return asbt::main();}

D. 车站

posted @ 2025-07-04 09:38  zhangxy__hp  阅读(42)  评论(0)    收藏  举报