2021牛客多校9(EI)

目录

E:Eyjafjalla(主席树 倍增 dfs序)

 I:Incentive Model(数学期望)


E:Eyjafjalla(主席树 倍增 dfs序)

题意:

给一棵树,1为根节点(火山口),每个结点都有一个温度权值t_{i},满足如果uv离根节点近,则t_{u}>t_{v}。q次询问,病毒在x结点爆发,只能在温度[l,r]存活传染,问最多能传染多少结点。

solution:

可以发现根节点的温度是最大的,并且根到每个叶子结点的链的权值都是递减的。

假设在x结点爆发,且只能在温度[l,r],可以向上找到一个最大温度祖宗结点(温度<=r),  我们只要求这个祖宗结点的所有子树中满足t_{i}>=l的结点个数,就是所求的答案。

因为每条向子节点的链都是递减的,且祖宗结点限制了上限,所以 t_{i}>=l的结点一定是可以都传染到的。 

需要特判一下如果在x结点爆发,且x结点不在温度[l,r],那答案就是0.

1.如何找到最大温度的祖宗结点?

可以使用倍增来加速这个过程。

2.如何求祖宗结点的所有子树中满足 t_{i}>=l的结点个数? 

 这个问题可以转换为:如果给一个数组序列,如何求这个序列大于某个值的个数?

解决这个问题的最好方法是主席树,可以快速求出答案。

那么所给出的是一颗树,并不是一个序列,如何转换为一个序列?

dfs序可以解决这个问题,因为一个子树其实就是一个连续序列。

比如一个结点的序列下标为x,那么它到子树的下标范围就为[x,x+siz[x]-1].(siz[x]为以x为根子树的大小)查询这个区间的大于等于l的个数即可。

把各个板子一套这道题就解决了

#include <bits/stdc++.h>
// #define int long long
using namespace std;
const int N=1e5+10;
int n;
struct node{
    int l,r;
    int sum;
}tre[N*40];
vector<int>edge[N];
int t1[N];
int t2[N];//离散化后的数组
int len;
int root[N],idx;
int id[N],w[N],siz[N],cnt;//每个结点的dfs的编号,温度,子树大小
int fa[N][20];//倍增数组
void lsh()
{
    sort(t1+1,t1+1+n);
    len=unique(t1+1,t1+1+n)-t1-1;
    for(int i=1;i<=n;i++)
    {
        t2[i]=lower_bound(t1+1,t1+1+len,t2[i])-t1;
    }
}
void dfs(int u,int fath)
{
    fa[u][0]=fath;
    for(int i=1;i<=17;i++)
    {
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    id[u]=++cnt,w[cnt]=t2[u],siz[u]=1;
    for(auto v:edge[u])
    {
        if(v==fath)
        continue;
        dfs(v,u);
        siz[u]+=siz[v];
    }
}
void  insert(int l,int r,int pre,int &now,int p)
{
    tre[++idx]=tre[pre];
    tre[now=idx].sum++;
    if(l==r)
    return;
    int mid=(l+r)>>1;
    if(p<=mid)
    insert(l,mid,tre[pre].l,tre[now].l,p);
    else
    insert(mid+1,r,tre[pre].r,tre[now].r,p);
}
int query(int l,int r,int sta,int end,int k)
{
    if(l==r)
    {
        return tre[end].sum-tre[sta].sum;
    }
    int mid=l+r>>1;
    int sum=tre[tre[end].r].sum-tre[tre[sta].r].sum;
    if(k<=mid)
    return sum+query(l,mid,tre[sta].l,tre[end].l,k);
    else
    return query(mid+1,r,tre[sta].r,tre[end].r,k);
}
int main()
{
    cin>>n;
    for(int i=0;i<n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&t1[i]);
        t2[i]=t1[i];
    }
    lsh();//离散化建主席树
    dfs(1,0);
    for(int i=1;i<=cnt;i++)
    {
        insert(1,cnt,root[i-1],root[i],w[i]);
    }
    int q;
    cin>>q;
    while(q--)
    {
        int x,l,r;
        scanf("%d%d%d",&x,&l,&r);
        l=lower_bound(t1+1,t1+1+len,l)-t1;
        r=upper_bound(t1+1,t1+1+len,r)-t1-1;
        if(t2[x]<l||t2[x]>r)
        {
            cout<<0<<endl;
            continue;
        }
        for(int i=17;i>=0;i--)
        {
            if(fa[x][i]&&t2[fa[x][i]]<=r)
            {
                x=fa[x][i];
            }
        }
        //在这个范围[x,x+siz[x]-1]
        cout<<query(1,n,root[id[x]-1],root[id[x]+siz[x]-1],l)<<endl;
    }
}

 I:Incentive Model(数学期望)

 傻逼题意(yue)

题意:

有A,B两人一起逐个挖n块矿,对于同一块矿,有a的概率属于A,有b的概率属于B,其中a,b为两方当时的股权占比。某方拥有一块矿后,其股权增加w。初始A有a的股权,B有1 - a的股权。求A能获得矿数的期望

solution:

不会数学期望... 参考其他人题解才懂

S_{i}为第i块挖出后A的期望股份,A获得第i块股份的概率为\frac{S_{i-1}}{1+w(i-1)}    (先开始一共有1的股权,第i块前一共有1+w(i-1)份股权)。

S_{i}=S_{i-1}+\frac{wS_{i-1}}{1+w(i-1)}  

S_{i-1}提出来化简为

S_{i}=S_{i-1}\frac{1+wi}{1+w(i-1)}

把所有项都拆开S_{i}=\frac{1+wi}{1+w(i-1)}\cdot \frac{1+w(i-1)}{1+w(i-2)}\cdot \frac{1+w(i-2)}{1+w(i-3)}\cdot \cdot \cdot \cdot \frac{1+w}{1}\cdot S_{0}

然后都能消掉

化简为S_{i}=\frac{1+wi}{1}*S_{0}

S_{0}=a

S_{i}=(1+wi)*a

题目要求矿数的期望,也就是A的期望股份S_{n}减去先开始的已有的股份a再除以w,即为答案

ans=n*a

\because a=\frac{x}{y} \therefore ans=n*\frac{x}{y}

求个逆元就行了

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int ksm(int a, int k)  // 求a^k mod p
{
    int res = 1 % mod;
    while (k)
    {
        if (k & 1) res = res * a % mod;
        a = a * a % mod;
        k >>= 1;
    }
    return res%mod;
}
signed main()
{
    int n,w,x,y;
    cin>>n>>w>>x>>y;
    int ans=(n*x)%mod;
    ans=ans*ksm(y,mod-2)%mod;
    cout<<ans%mod;
   
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值