CSP模拟10

傻了,打了半天的代码,一分没有。

A. B

题目链接

这个题一开始没看懂题,以为是任意的连通图,结果后来才注意到那个 菊花图。

其实就很简单,基础的组合数学,有高中知识就行。

考虑选的两个点,有两种情况:

  1. 一个花蕊一个花瓣;
  2. 两个花瓣;

两个花瓣的概率为 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;
} 
View Code

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);
}
View Code

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
*/
View Code

 

posted @ 2023-07-31 21:49  Trmp  阅读(23)  评论(0)    收藏  举报
-->