CSP模拟10
傻了,打了半天的代码,一分没有。
A. B
这个题一开始没看懂题,以为是任意的连通图,结果后来才注意到那个 菊花图。
其实就很简单,基础的组合数学,有高中知识就行。
考虑选的两个点,有两种情况:
- 一个花蕊一个花瓣;
- 两个花瓣;
两个花瓣的概率为 C(n-1,2)/C(n,2) ,期望为 C(n-1,2)/(n,2)*2;
剩下的概率乘补集就行;
化简完为 (1+(n-2)/n)

#include <iostream> #include <cstdio> using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); while(ch>'9' || ch<'0') { if(ch=='-') { f=-1; } ch=getchar(); } while(ch>='0' && ch<='9') { x=(x<<3)+(x<<1)+ch-'0'; ch=getchar(); } return f*x; } int T; long double ans1,ans2,n; int main() { T=read(); while(T--) { scanf("%Lf",&n); if(n==2) { ans1=1.0; ans2=1.0; } else { ans1=1.0+(n-2)/n; ans2=2.0; } printf("%.9Lf %.9Lf\n",ans1,ans2); } return 0; }
B. L
打了好久的代码,一分没有 ,呜呜呜呜。
既然让极差最小,可以想到把所选的元素尽量的集中起来,这样就可以让极差最小。
所以我们可以把每个集合内升序排序,然后把第一个一个一个一个集合里的每一个元素单拿出来,在剩下的集合里找与这个元素找相邻的元素,左边的作为最小值,右边的作为最大值。
压成一个结构体,再按最大值降序排序,从头开始遍历,在指针前进过程中,维护最小值得最小值,指针指向最大值,相减与ans取min。
原理就是在指针在向右前进时,指针前面的全部选小值,这样就不会对当前的maxn造成影响,指针后面的元素全部选大值(或者小值,只要不对当前的maxn和minn造成影响就行);
复杂度O(k+c);

#include <iostream> #include <cstdio> #include <algorithm> #include <vector> #include <cstring> #define int long long const int N=1e6+10; const int inf=(1ll<<62); using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); while(ch>'9' || ch<'0') { if(ch=='-') { f=-1; } ch=getchar(); } while(ch>='0' && ch<='9') { x=(x<<3)+(x<<1)+ch-'0'; ch=getchar(); } return f*x; } vector<int> s[N]; int c[N],b[N],d[N]; struct node { int minn,maxn; }t[N]; int k; int ans=inf; void work(int d){ int ma,mi; for(int i=2;i<=k;i++){ while(s[i][b[i]]<d && b[i]<=c[i]){ b[i]++; } ma=s[i][b[i]],mi=s[i][b[i]-1]; t[i-1].minn=mi,t[i-1].maxn=ma; } } bool cmp(node a,node b){ return a.maxn>b.maxn; } signed main(){ k=read(); for(int i=1;i<=k;++i){ c[i]=read(); for(int j=1;j<=c[i];++j){ d[j]=read(); } sort(d+1,d+c[i]+1); s[i].push_back(-ans); for(int j=1;j<=c[i];++j){ s[i].push_back(d[j]); } s[i].push_back(ans); } for(int i=1;i<=c[1];++i){ int now=s[1][i]; work(now); sort(t+1,t+k,cmp); int mi=now; for(int j=1;j<k;j++){ ans=min(t[j].maxn-mi,ans); mi=min(mi,t[j].minn); } } printf("%lld",ans); }
C. U
喊出我们的口号:概率期望,狗都不做;
首先我们知道 期望等于概率乘上权值;
然后先考虑k=2的情况,设fi,j表示 i 和 j 之间的路径权值,那么它的期望就为 fi,j*/C(n,2);
再考虑k>2的情况,权值不变,概率变为了 C(n-2,k-2)/C(n,k);
那么答案就是 C(n-2,k-2)*fi,j总/C(n,k)
现在要求fi,j总,那么考虑每条边的贡献。
首先下放边权,这样好处理。然后我们发现,一条路径(x,y)如果想要经过这个点 u,其中一个端点 x 一定在 u的子树里,另个端点 y 一定不在这棵子树里。
求出来x和y可以的位置的个数,再一相乘,就是这条路径被经过的次数,也就是贡献。
我们发现这个贡献可能重复,因为可能有一样的边权,这时就要控制x,y的运动范围了。
考虑一个点的管辖区域,分为向下和向上,所谓的管辖就是区域内没有和当前点权值一样的一整块区域。

#include <iostream> #include <cstdio> #define int long long const int MAXN=4e5+10; const int mod=998244353; using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); while(ch>'9' || ch<'0') { if(ch=='-') { f=-1; } ch=getchar(); } while(ch>='0' && ch<='9') { x=(x<<3)+(x<<1)+ch-'0'; ch=getchar(); } return f*x; } int n,cnt; struct node { int next,to,val; }a[MAXN<<1]; int head[MAXN]; int jc[MAXN],inv[MAXN]; void add(int u,int v,int w) { a[++cnt].to=v; a[cnt].val=w; a[cnt].next=head[u]; head[u]=cnt; } int fa[MAXN],sum[MAXN],siz[MAXN]; int val[MAXN],pos[MAXN],top[MAXN]; int ok[MAXN],tot[MAXN],ans[MAXN]; int total,answer; void dfs1(int now,int father) { fa[now]=father; siz[now]=1; for(int i=head[now];i;i=a[i].next) { int v=a[i].to; if(v==father) continue; val[v]=a[i].val; dfs1(v,now); siz[now]+=siz[v]; } sum[now]=siz[now]; } void dfs2(int now) { top[now]=pos[val[now]]; pos[val[now]]=now; sum[top[now]]-=siz[now]; for(int i=head[now];i;i=a[i].next) { int v=a[i].to; if(v==fa[now]) continue; dfs2(v); } pos[val[now]]=top[now]; } int fpow(int x,int k) { int res=1; while(k) { if(k&1) res=res*x%mod; x=x*x%mod; k>>=1; } return res; } int C(int n,int m) { if(n<m) return 0; return jc[n]%mod*inv[n-m]%mod*inv[m]%mod; } void init() { jc[0]=1,inv[0]=1; for(int i=1;i<=MAXN;i++) { jc[i]=jc[i-1]*i%mod; inv[i]=fpow(jc[i],mod-2)%mod; } } signed main() { n=read(); for(int i=1;i<n;i++) { int u=read() , v=read() , w=read(); add(u,v,w); add(v,u,w); } init(); dfs1(1,1); dfs2(1); for(int i=1;i<=n;i++) { tot[val[i]]+=sum[i]; } for(int i=1;i<=n;i++) { if(top[i]) { ok[i]=sum[top[i]]; } else { ok[i]=siz[1]-tot[val[i]]; } } for(int i=1;i<=n;i++) { total=(total+ok[i]%mod*sum[i])%mod; } for(int k=2;k<=n;k++) { ans[k]=C(n-2,k-2)*total%mod*fpow(C(n,k),mod-2)%mod; answer^=ans[k]; } printf("%lld",answer); return 0; } /* 2 1 2 1 */ /* 3 1 2 1 1 3 2 */