目录
E:Eyjafjalla(主席树 倍增 dfs序)
题意:
给一棵树,1为根节点(火山口),每个结点都有一个温度权值,满足如果比离根节点近,则。q次询问,病毒在x结点爆发,只能在温度存活传染,问最多能传染多少结点。
solution:
可以发现根节点的温度是最大的,并且根到每个叶子结点的链的权值都是递减的。
假设在x结点爆发,且只能在温度,可以向上找到一个最大温度祖宗结点(温度<=r), 我们只要求这个祖宗结点的所有子树中满足>=的结点个数,就是所求的答案。
因为每条向子节点的链都是递减的,且祖宗结点限制了上限,所以 >=的结点一定是可以都传染到的。
需要特判一下如果在x结点爆发,且x结点不在温度,那答案就是0.
1.如何找到最大温度的祖宗结点?
可以使用倍增来加速这个过程。
2.如何求祖宗结点的所有子树中满足 >=的结点个数?
这个问题可以转换为:如果给一个数组序列,如何求这个序列大于某个值的个数?
解决这个问题的最好方法是主席树,可以快速求出答案。
那么所给出的是一颗树,并不是一个序列,如何转换为一个序列?
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:
不会数学期望... 参考其他人题解才懂
令为第i块挖出后A的期望股份,A获得第i块股份的概率为 (先开始一共有1的股权,第i块前一共有1+w(i-1)份股权)。
故
把提出来化简为
把所有项都拆开
然后都能消掉
化简为
即
题目要求矿数的期望,也就是A的期望股份减去先开始的已有的股份再除以,即为答案
求个逆元就行了
#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;
}