题目链接:https://cometoj.com/contest/37/problems
A.因自过去而至的残响起舞
对于爆炸型增长的数来说,增长到x只需要log级别就ok了,所以直接暴力就好了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
const int mod = 1e9 + 7;
ll sum[mx];
int main(){
ll n;
scanf("%lld",&n);
sum[1] = 1;sum[2] = 2;
if(n==1) return 0*puts("2");
int c;
for(int i=3;i<mx;i++){
sum[i] = sum[i-1];
sum[i] += sum[i-1]/2;
if(sum[i]>n) return 0*printf("%d\n",i);
}
return 0;
}
B.她的想法、他的战斗
一开始傻了,没有看到确定p,所以以为是二重积分。
实际上p肯定是在[l,r]中的,最后的答案肯定也就是在[l,r]中取一个值小于等于p的概率乘上(L+R)/2-p就是答案了。
(L+R)/2也就是第二个区间的期望取值(可以取积分,也可以直接根据对半性一眼就看出来)。
最后式子可写为:ans = (p-l)/(r-l)*((L+R)/2-p),实际上就是去解(p-l)*((L+R)/2-p)的一元二次次最大值,最后再看最大值是否在[l,r]中。不在的话,根据二元一次的性质最大值肯定是取p = r的时候。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
const int mod = 1e9 + 7;
int main(){
double l,r,L,R;
scanf("%lf%lf%lf%lf",&l,&r,&L,&R);
double ans;
double w = (R+L)/2;
double ret = (4*w*l-(w+l)*(w+l))/-4;
double x = (w+l)/2;
if(l<=x&&x<=r) ans = max(ans,ret/(r-l));
else ans = max(ans,w-r);
printf("%.4lf\n",ans);
return 0;
}
C.言论的阴影里妄想初萌
期望也就是==数量的概率*数量。那么我们把所有个集合都看成单个,答案就是所有的概率之和。也就是:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
const int mod = 998244353;
int n;
ll fac[mx];
ll qpow(ll x,ll y){
ll ans = 1;
while(y){
if(y&1) ans = ans*x%mod;
x = x*x%mod;
y >>= 1;
}
return ans;
}
ll C(int N,int M){
return fac[N]*qpow(fac[M],mod-2)%mod*qpow(fac[N-M],mod-2)%mod;
}
int main(){
ll x,y;fac[0] = 1;
for(int i=1;i<mx;i++) fac[i] = fac[i-1]*i%mod;
scanf("%d%lld%lld",&n,&x,&y);
ll ans = 1,p = x*qpow(y,mod-2)%mod;
for(int i=1;i<=n;i++){
ans += qpow(p,1ll*i*(i-1)/2)*C(n,i)%mod;
ans %= mod;
}
printf("%lld\n",ans);
return 0;
}
D.错综的光影所迷惑的思念是
对于一颗树来说,它的直径也就对应一个中心点p,也就是所有的直径必然都会经过点p(很好证明)
所以我们就可以枚举中心点p,然后再枚举半径就可以解决直径长度为偶数的情况,但是对于直径长度为奇数的情况来说就有点麻烦了,此时可以把每个边变成两倍,也就是中间再开一个点,这样的话从额外点出发做中心点的话计算的就是原直径长度为奇数的情况,从原来点做中心出发计算的就是原直径长度为偶数的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 4e3 + 10;
const int mod = 998244353;
int n,ans[mx],dep[mx][mx];
int pre[mx];
ll fac[mx];
vector <int> g[mx];
void dfs(int u,int d,int fa,int *p)
{
if(u<=n) p[d]++;
for(int v:g[u])
if(v!=fa) dfs(v,d+1,u,p);
}
int main(){
scanf("%d",&n);
int u,v;fac[0] = 1;
for(int i=1;i<mx;i++) fac[i] = (fac[i-1]<<1)%mod;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
g[v].push_back(i+n);
g[i+n].push_back(v);
g[u].push_back(i+n);
g[i+n].push_back(u);
}
for(int i=1;i<2*n;i++){
memset(pre,0,sizeof(pre));
int is = 0;
for(int j:g[i]){
memset(dep[is],0,sizeof(dep[is]));
dfs(j,1,i,dep[is]);
for(int k=1;k<n;k++) pre[k] += dep[is][k];
is++;
}
pre[0] = i>n?0:1;
for(int j=1;j<n;j++) pre[j] += pre[j-1];
for(int j=1;j<n;j++){
int c = fac[pre[j]-pre[j-1]] - 1;
for(int k=0;k<is;k++) c = (c-(fac[dep[k][j]]-1))%mod;
c = (c+mod)%mod;
ans[j] += c*fac[pre[j-1]]%mod;
ans[j] %= mod;
}
}
for(int i=1;i<n;i++) printf("%d\n",ans[i]);
return 0;
}
E.情报强者追逐事件
很明显我们可以把这些看做若干个基环内向树,首先考虑环外树的情况,肯定是可以通过dp获得他们的ans值,然后我们将环外树的情况都考虑之后将树上的信息都合并到环中,也就是改变环中节点的p[i],然后接下来再看环中的点(假设点的个数为n),将环断开为链然后复制一遍,考虑第i个的答案i∈[n+1,2*n],i肯定是从[i-n+1,i-1]这段中更新过来的,这个我们可以通过枚举以i为起始点往左边看第一个自己醒来的点是哪个,然后将这些值相加就是i点被环中的点叫醒的概率。
对于每个点i我们肯定不能用O(n)去找出它的环上答案,因此我们可以用一个后缀和的形式,最后将得到的后缀和除以他们的公共后缀就是区间[i-n+1.i-1]的贡献和了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 998244353;
int n;
ll p[mx],in[mx],s[mx];
ll q[mx],w[mx],ans[mx];
ll suf[mx],rv[mx];
vector <int> g[mx];
ll qpow(ll x,ll y){
ll ans = 1;
while(y){
if(y&1) ans = ans*x%mod;
y >>= 1;
x = x*x%mod;
}
return ans;
}
void torpu(){
queue <int> qu;
for(int i=1;i<=n;i++){
if(!in[i]) qu.push(i);
}
while(!qu.empty()){
int now = qu.front();
qu.pop();
ans[now] = p[now];
for(int v:g[now]){
in[v]--;
if(!in[v]) qu.push(v);
p[v] = (1-p[v])*(1-ans[now]*s[now]%mod)%mod;
p[v] = (1-p[v]+mod)%mod;
}
}
}
int Next(int u){
for(int v:g[u])
if(in[v]) return v;
}
int main(){
scanf("%d",&n);
int u,v;
for(int i=1;i<=n;i++){
scanf("%d%d",&v,&u);
p[i] = v*qpow(u,mod-2)%mod;
}
for(int i=1;i<=n;i++){
scanf("%d",&u);
g[i].push_back(u);
in[u]++;
}
for(int i=1;i<=n;i++){
scanf("%d%d",&v,&u);
s[i] = v*qpow(u,mod-2)%mod;
}
torpu();
for(int i=1;i<=n;i++) if(in[i])
{
int u = i,v = Next(u),is = 0;
q[++is] = u;
while(v!=u){
in[v] = 0;
q[++is] = v;
v = Next(v);
}
for(int i=1;i<=is;i++){
q[i+is] = q[i];
rv[i] = rv[i+is] = qpow(p[q[i]],mod-2)*(1-p[q[i]]+mod)%mod;
}
suf[2*is] = w[2*is] = p[q[is]];
for(int i=2*is-1;i;i--){
w[i] = w[i+1]*rv[i+1]%mod;
w[i] = w[i]*p[q[i]]%mod*s[q[i]]%mod;
suf[i] = (w[i]+suf[i+1])%mod;
}
for(int i=1;i<is;i++){
ll ret = suf[i+1]-suf[i+is];
ret = (ret+mod)%mod;
ll tmp = w[i+is+1]*s[q[i]]%mod*rv[i+is+1]%mod;
ans[q[i]] = ret*qpow(tmp,mod-2)%mod;
ans[q[i]] = (ans[q[i]]+p[q[i]])%mod;
}
ans[q[is]] = (suf[is+1]-suf[2*is]+mod)%mod;
ans[q[is]] = (ans[q[is]]+p[q[is]])%mod;
}
for(int i=1;i<=n;i++) printf("%lld%c",ans[i],i==n?'\n':' ');
return 0;
}