C-Edgy Trees
给定一个有红边和黑边的树,求经过至少一条黑边的点序列的总数。直接考虑它的反面就是就是一条黑边也没有走过的点序列的总数,统计树上所有被黑边断开的连通块。答案显然就是 n k − ∑ s z k n^{k}-∑sz^{k} nk−∑szk 。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const ll mod=1e9+7;
const int N=1e5+50;
bool vis[N];
vector<int> G[N];
ll fpow(ll x,ll y) {
ll ans=1;
while(y) {
if(y&1) ans*=x,ans%=mod;
x*=x,x%=mod;
y>>=1;
}
return ans;
}
ll dfs(int u) {
vis[u]=1;
ll sz=1;
for(auto &v:G[u]) {
if(!vis[v])
sz+=dfs(v);
}
return sz;
}
int main() {
ll n,k;
scanf("%lld%lld",&n,&k);
for(int i=1;i<n;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(w==0) {
G[u].push_back(v);
G[v].push_back(u);
}
}
ll sum=fpow(n,k);
for(int i=1;i<=n;i++) {
if(vis[i]) continue;
ll sz=dfs(i);
sum=(sum+mod-fpow(sz,k))%mod;
}
printf("%lld\n",sum);
return 0;
}
D-Steps to One
首先给定一个整数n,然后我每次都会从一个空集里面添加一个元素,这个元素是从1到n等概率随机添加。然后当我添加到这个集合里面所有数的最大公约数为1的时候就停下来,代表这个游戏结束了。然后要求这个集合大小的期望。
那么这里可以考虑的是如果我集合里面的gcd为x的情况的时候还要多少次才能让游戏结束,有
d
p
[
x
]
=
1
+
1
n
∑
i
=
1
n
d
p
[
g
c
d
(
i
,
x
)
]
dp[x]=1+\frac{1}{n}∑_{i=1}^{n}dp[gcd(i,x)]
dp[x]=1+n1∑i=1ndp[gcd(i,x)],然后显然
d
p
[
1
]
=
0
dp[1]=0
dp[1]=0,并且有最终答案是
a
n
s
=
1
+
1
n
∑
i
=
1
n
d
p
[
i
]
ans=1+\frac{1}{n}∑_{i=1}^{n}dp[i]
ans=1+n1∑i=1ndp[i]。
然而注意到即使这里转移的时候复杂度仍然是
O
(
n
2
)
O(n^2)
O(n2),所以这里要对这个dp做一些优化的工作。
枚举i先转化为枚举约数:
d
p
[
x
]
=
1
+
1
n
∑
d
∣
x
d
p
[
d
]
∗
(
∑
i
=
1
n
g
c
d
(
i
,
x
)
=
=
d
)
dp[x]=1+\frac{1}{n}∑_{d|x}dp[d]*(∑_{i=1}^{n}gcd(i,x)==d)
dp[x]=1+n1∑d∣xdp[d]∗(∑i=1ngcd(i,x)==d) (1)
但是这当中包含了d就是x的情形,我们分离出来移项:
d
p
[
x
]
−
[
n
x
]
1
n
d
p
[
x
]
=
1
+
1
n
∑
d
∣
x
d
!
=
x
d
p
[
d
]
(
∑
i
=
1
n
g
c
d
(
i
,
x
)
=
=
d
)
dp[x]-[\frac{n}{x}]\frac{1}{n}dp[x]=1+\frac{1}{n}∑_{d|x\space d!=x}dp[d](∑_{i=1}^{n}gcd(i,x)==d)
dp[x]−[xn]n1dp[x]=1+n1∑d∣x d!=xdp[d](∑i=1ngcd(i,x)==d) (2)
因为在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)处理出所有的数的约数是方便的,我们只要考虑括号里面的式子,注意到可以把d给约去,括号里面的式子就等价于:
∑
i
=
1
[
n
d
]
g
c
d
(
i
,
x
d
)
=
=
1
∑_{i=1}^{[\frac{n}{d}]}gcd(i,\frac{x}{d})==1
∑i=1[dn]gcd(i,dx)==1 (3)
而这个恰好就满足了莫比乌斯函数的定义:
∑
i
=
1
[
n
d
]
∑
t
∣
g
c
d
(
i
,
x
d
)
μ
(
t
)
∑_{i=1}^{[\frac{n}{d}]}∑_{t|gcd(i,\frac{x}{d})}\mu(t)
∑i=1[dn]∑t∣gcd(i,dx)μ(t) (4)
再做一些化简就能够得到
∑
t
∣
x
d
μ
(
t
)
∗
[
m
d
t
]
∑_{t|\frac{x}{d}}\mu(t)*[\frac{m}{dt}]
∑t∣dxμ(t)∗[dtm] (5)
回代到原来的式子里面就能求出来了,应该说莫比乌斯反演来做这个还是相对比较方便的。而题解给了一种容斥的做法,代码比较复杂。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const ll mod=1e9+7;
const int N=1e5+50;
vector<ll> factor[N];
ll mu[N],pri[N],dp[N],m;
int cnt=0;
bool vis[N];
void getmu() {
mu[1]=1;
for(int i=2;i<=(int)1e5;i++) {
if(!vis[i]) pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&pri[j]*i<=(int)1e5;j++) {
vis[i*pri[j]]=true;
if(i%pri[j]==0) {
mu[i*pri[j]]=0;
break;
}
else mu[i*pri[j]]=-mu[i];
}
}
}
ll getinv(ll x) {
ll y=mod-2,ans=1;
while(y) {
if(y&1) ans*=x,ans%=mod;
x*=x,x%=mod;
y>>=1;
}
return ans;
}
void getfactor() {
for(ll i=1;i<=(ll)1e5;i++)
for(ll j=i;j<=(ll)1e5;j+=i)
factor[j].push_back(i);
}
ll gsum(ll x,ll d) {
ll sum=0;
for(auto t:factor[x/d]) sum+=mu[t]*(m/(d*t)),sum%=mod;
return sum;
}
int main() {
getmu();
getfactor();
scanf("%lld",&m);
dp[1]=0;
ll ans=0;
for(ll i=2;i<=m;i++) {
ll rhs=m;
for(auto fac:factor[i]) {
if(fac==i) continue;
ll sum=gsum(i,fac);
rhs+=dp[fac]*sum;
rhs%=mod;
}
dp[i]=rhs*getinv(m-m/i),dp[i]%=mod;
ans+=dp[i],ans%=mod;
}
printf("%lld\n",1+ans*getinv(m)%mod);
return 0;
}
E-Maximize Mex
是说有n个学生和m个社团,每个学生都有一个潜力值和一个id表示它是属于哪一个社团的。然后这里给你q次查询是说每次都会踢掉一个人,然后每个社团这个时候都会从这个社团里面最多选出来一个人,当然没有人就不会选,要求这些人的潜力值的mex,然后这个mex就是转移SG函数的时候用的那个mex,一个不属于当前集合的最小非负元素。
既然要让mex更大,其实一个贪心的想法就是要让我现在的一个集合先尽量地大,如果我能够在我现在的状态下多添加一个元素,那么mex必定是单调不劣。
然后这里又因为每个社团最多选取一个学生,所以就从潜力值往社团连边,求最大匹配。显然删边的操作并不容易做,不如离线之后加边。用一个匈牙利来维护最大匹配就好了。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=5e3+50;
int p[N],id[N],qry[N];
bool vis[N];
bool used[N];
int nxt[N];
vector<int>G[N];
int ans[N];
bool Find(int u) {
if(used[u]) return false;
used[u]=1;
for(auto &v:G[u]) {
if(nxt[v]==-1||Find(nxt[v])) {
nxt[v]=u;
return true;
}
}
return false;
}
int main() {
memset(nxt,-1,sizeof(nxt));
int n,m,q;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
for(int i=1;i<=n;i++) scanf("%d",&id[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++) {
scanf("%d",&qry[i]);
vis[qry[i]]=1;
}
for(int i=1;i<=n;i++) {
if(!vis[i]) {
G[p[i]].push_back(id[i]);
}
}
int u=0;
for(int i=q;i>=1;i--) {
memset(used,0,sizeof(used));
while(Find(u)) {
++u;
memset(used,0,sizeof(used));
}
ans[i]=u;
G[p[qry[i]]].push_back(id[qry[i]]);
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}