不难想到,要求环的期望,只需求出所有可能的环的长度总和和不相邻点对的组数。而边数确定,则只需求环的总长。对于两个不相邻的点x,y,所形成的环的长度等于两点深度之差加一,\(\vert dp[x]-dp[y]\vert+1\),不妨令x为根节点,则只需求所有节点的深度之和,再减去相邻的点,最后对树进行换根dp,输出答案即可。
另一道树上问题
题意:给一棵n个节点和n-1条边的树,均匀随机地选取两不相邻的点,求出现的环的期望长度。
代码
#include <bits/stdc++.h>
#define int long long
#define MOD 1000000007
using namespace std;
int f[1000086]={0},n,sum=0;
vector<int>g[1000086],vis(1000086,0),vis1(1000086,0);
vector<int>cnt(1000086,0);
void dfs(int x,int s){
vis[x]=1;
cnt[x]=1;
int y=0;
for(int i=0;i<g[x].size();i++){
if(!vis[g[x][i]]){
dfs(g[x][i],s+1);
cnt[x]+=cnt[g[x][i]];
y+=f[g[x][i]];
vis[g[x][i]]=0;
}
}
f[x]+=y;
f[x]+=s;
}
void dfs1(int x){
vis1[x]=1;
for(int i=0;i<g[x].size();i++){
if(!vis1[g[x][i]]){
f[g[x][i]]=f[x]+n-cnt[g[x][i]]*2;
sum+=f[g[x][i]]-g[g[x][i]].size()*2+n-1;
dfs1(g[x][i]);
vis1[g[x][i]]=0;
}
}
}
int32_t main() {
cin >> n;
for(int i=0;i<n-1;i++){
int x;
cin>>x;
g[i+1].emplace_back(x);
g[x].emplace_back(i+1);
}
dfs(n,0);
sum+=f[n]-g[n].size()*2+n-1;
dfs1(n);
sum/=2;
int t=(n-1)*(n-2)/2;
int gg=gcd(t,sum);
cout<<sum/gg<<'/'<<t/gg<<endl;
}
第一次遇到限制空间的题,故一直RE,最后是卡空间过的。实际上可以遍历两次来节约空间,若存在\(a_i=a_j\),因为数列是递推的,则必存在\(a_k=a_n\),则可以先遍历一遍算出\(a_n\),再遍历第二遍来判断是否存在\(a_k=a_n\),这样就不需要存储计算过的\(a_i\)来判重。
随机序列检测
题意:此题空间限制为4MB。整数序列满足\(a_i = (Aa_{i−1}^2 + Ba_{i−1} + C) mod P, i > 0\),给定$ n, a_0, A, B, C, P$,判定这个序列中是否存在重复的数字?
代码
#include <bits/stdc++.h>
#define ll long long
#define MOD 998244353
using namespace std;
map<int,int>hm;
ll n,A,B,C,f=0;
int P,a0,an;
int32_t main() {
int T = 1;
cin >> T;
while (T--) {
cin>>n>>a0>>A>>B>>C>>P;
an=a0;
f=0;
for(int i=1;i<=n;i++){
an=(((A*an)%P*an)%P+(B*an)%P+C)%P;
}
if(a0==an){
f=1;
}
for(int i=1;i<=n-1;i++){
a0=(((A*a0)%P*a0)%P+(B*a0)%P+C)%P;
if(a0==an||f){
f=1;
break;
}
}
if(f)cout<<"Repetitive\n";
else cout<<"Different\n";
}
}