一、life
题目大意:给你n扇门,每扇门i可以任意传送到1~i这i扇门中的一扇,你现在在第n扇门,问你到达第一扇门的期望步数。为了防止精度误差,请你给出答案对1e9+7取模的结果。
首先这道题一定是用期望来算,我们设计状态f【i】表示从n到i的期望步数,根据期望的定义,我们容易知道,如果一扇门i可以到另一扇门j,那么一定存在f【j】+=(f【i】+1)/i。但是你发现这样正着推每一个状态可以由它自己和它之后的状态推出,因为只能由后面的门到前面的,但是你在推式子的时候发现,当i等于1的时候,i-1=0,而0是没有逆元的。并且正着推很不方便,所以我们采用倒着推得方法。重新设计状态f【i】表示从i到1的期望步数,容易发现f【i】=Σ(f【j】+1)/i (j∈【1,i】),然后因为门自己可以到自己,但是我们又不清楚f【i】在更新前是什么,所以我们可以移项相消,最后我们得到(i-1)/i*f【i】=1/i*Σf【j】+(i-1)/i。然后我们把(i-1)/i给除过去,容易发现这是个前缀和,所以我们维护前缀和之后就可以O(1)求f。那么目前需要解决的问题就是求乘法逆元了。之前我们求一个数关于模的乘法逆元是使用费马小定理可以log时间内求,但是这个题我们是要求1-n内所有数的逆元,这样显然不现实。所以我们使用O(n)预处理所有逆元的方法。关于这个怎么证明,
于是我们就写出了这道题。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e7+10;
const int mod=1e9+10;
ll n,sum,f[maxn],inv[maxn];
int main(){
scanf("%lld",&n);
inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=(mod-mod/i*inv[mod%i]%mod)%mod;
f[1]=0;
for(int i=2;i<=n;i++){
f[i]=(sum*inv[i-1]%mod+i*inv[i-1]%mod)%mod;
}
printf("%lld\n",f[n]);
return 0;
}
二、zhttty
题目大意:给了你一棵树,然后树上的每两个距离为2的点之间连一条无向边,求ΣΣdis(i,j)/2,dis(i,j)为i与j之间的最短路径。n的范围为2e6,显然你得想到一个O(n)的算法。不过首先拿五十分很容易,肝正解好像需要不少时间 ,那就先敲暴力,反正最后你也要用暴力来对拍。五十分做法:暴力建边,然后以每个点为起点跑最短路,答案累加除以2。复杂度O(n^2logn)。100分做法:考虑树形DP。我们先考虑不加边的O(n)求所有点对距离和的方法。维护三个数组,dp1【i】表示以i为根的子树大小,然后对每条边求它的贡献,每一条边中的y,ans1+=(n-dp1【y】)*dp1【y】。就O(n)求出了ans1.然后我们发现,任意两个点对如果深度一个为奇,一个为偶,那么他们的最短距离一定是之前的路径长度+1再除以2.然后我们就预处理出来奇数深度的点的个数和偶数深度的点个数,把他们相乘,加上ans1再除以2就是答案。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e6+10;
int n,tot,ver[maxn<<1],Next[maxn<<1],lin[maxn],siz[maxn],dep[maxn],fa[maxn];
ll ans,f1,f2;
void add(int x,int y){
ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;
}
void dfs(int x,int f){
dep[x]=dep[f]+1;siz[x]=1;
if(dep[x]%2==1) f1++; else f2++;
for(int i=lin[x];i;i=Next[i]){
int y=ver[i];
if(y==f) continue;
dfs(y,x);
siz[x]+=siz[y];
}
}
void dfs1(int x,int f){
for(int i=lin[x];i;i=Next[i]){
int y=ver[i];
if(y==f) continue;
ans+=siz[y]*(n-siz[y]);
dfs1(y,x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1,0);
dfs1(1,0);
ans=(ans+f1*f2)/2;
printf("%lld\n",ans);
return 0;
}
三、奖励点数
大致题意:给你t组数据,每次给定a、b、c,你要把0经过一些操作变成c,每次+1或-1的代价为a,乘2的代价为b,求最小代价。这道题是个数位dp,暂时不做总结