The 2023 ICPC Asia Hangzhou Regional Contest (The 2nd Universal Cup. Stage 22)H - Sugar Sweet II

题意:

输入T组,每组输入n个,接下来3行每行n个整数,第一行代表初始糖果数量,第二行代表对应的编号,第三行代表满足条件的奖励糖果数量,要求:在任意事件发生的顺序下,每个人能得到的糖果数量的期望(如果当前编号的糖果数小于对应编号的糖果数,当前编号获得奖励糖果)

思路:

每个人有三种情况:

①:直接得到奖励糖果

②:对应编号糖果得到奖励糖果之后才能得到奖励糖果

③:无论如何都不能得到奖励糖果

①③无需考虑事件发生顺序

②:假设编号A得到奖励糖果之后,B才能得到奖励糖果,按照事件发生顺序来说,A必须发生在B之前

也就是说,编号B如果想要获得奖励一定要在A之后发生,否则则没有限制

最后得到第一个结论:B得到糖果的期望等于:P(B发生在A之前)*(B原来糖果数量)+P(B发生在A之后)*(B原来糖果数量*B奖励糖果数量)

然后在前面的基础上,假设A又要在C得到奖励糖果之后才能得到奖励糖果,那么C又必须在A之前,也就是说B要获得奖励糖果,要满足C->A->B的顺序

那么此时B得到糖果的期望等于:P(C->A->B)*(B原来糖果数量)+P(!(C->A->B))*(B原来糖果数量+B奖励糖果数量)(PS:P(C->A->B)的意思是满足发生顺序为C->A->B这个顺序的概率,P(!(C->A->B))意思是1-P(C->A->B))

以此类推,所以这个先后发生的关系其实就是一个依赖关系,所以可以把这个依赖转化成一个图论问题A依赖于B,那么就连一条(B-->A),按照拓补序,没有依赖的点(也就是入度为0的点)优先弹出,那么每个点在弹出的时候就可以算在他之前有多少个依赖,那么期望就可以求出来了

举例:假设a为未获得奖励的糖果,b为获得奖励的糖果,m为前面的依赖个数,那么该位置的期望就为:

P(成功)的则为前面m个数字都按照顺序排列的概率,也就是m!

最后就有:(a+b)*P(成功)+a*(1-P(成功))

代码实现:

#include <map>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define pp pop_back()
#define int long long
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e6+10,INF=4e18;
const int mod=1e9+7;
int n;
int a[N],b[N],w[N],ans[N];
int h[N],ne[N],e[N],idx,dep[N],in[N];
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int A[N];
void init()
{
	A[0]=1;
	for(int i=1;i<=500000;i++)
		A[i]=A[i-1]*i%mod;
	return ;
}
int qmi(int a,int k,int p){
	int res=1;
	while(k){
		if(k&1) res=res*a%p;
		a=a*a%p;
		k>>=1;
	}
	return res;
}
int getAns(int x,int y){
	return qmi(y,mod-2,mod)*x%mod;
}
void topsort()
{
	queue<int>q;
	_rep(i,1,n)
		if(!in[i]&&a[i]<a[b[i]])in[i]--,q.push(i),dep[i]=1;
	while(q.size())
	{
		auto u=q.front();
		q.pop();
		for(int i=h[u];~i;i=ne[i])
		{
			int j=e[i];
			if(--in[j]==0)
			{
				q.push(j);
				dep[j]=dep[u]+1;
			}
		}
	}
	_rep(i,1,n)
	{
		int now=A[dep[i]];
		if(dep[i])ans[i]=getAns(((((now-1+mod)%mod)*a[i]%mod+a[i])%mod+w[i])%mod,now);
		else ans[i]=0;
	}
	return;
}
void solve()
{
	cin>>n;
	idx=0;
	memset(h,-1,(n+1)*sizeof(int));
	_rep(i,1,n) cin>>a[i],ans[i]=0,dep[i]=0,in[i]=0;
	_rep(i,1,n) cin>>b[i];
	_rep(i,1,n) cin>>w[i];
	_rep(i,1,n){
		if(b[i]!=i&&a[i]>=a[b[i]]&&a[i]<a[b[i]]+w[b[i]])
		{
			add(b[i],i),in[i]++;
		}
	}
	topsort();
	_rep(i,1,n){
		if(!ans[i])ans[i]=a[i];
		if(i==1) cout<<ans[i];
		else cout<<" "<<ans[i];
	}
	cout<<endl;
}
signed main()
{
	IOS;
	init();
	int T=1;
	cin>>T;
	while(T--)
		solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值