CF997C Sky Full of Stars
求的网格内每个格子染三种颜色,其中至少有一行或一列是同一种颜色的方案数。
转化为求没有一行或一列颜色相同。
直接枚举相同的行列数进行容斥:
后面的那个式子是因为和有一个为的时候相同的染色行列不一定只有三种方案。
#include<bits/stdc++.h>
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
int n;
int Pow(int b,LL k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod;return r; }
int main(){
scanf("%d",&n);
int ans = 3ll * Pow(3,n*1ll*n) % mod , C = 1;
rep(i,0,n){
ans = (ans - 3ll * C * (i&1 ? -1ll : 1ll) * Pow(Pow(3,n-i)-1,n)) % mod;
if(i) ans = (ans - 2ll * C * (i&1 ? -1ll : 1ll) * Pow(3,1ll*n*(n-i)) % mod * (Pow(3,i) - 3)) % mod;
C = 1ll * C * (n-i) % mod * Pow(i+1,mod-2) % mod;
}
printf("%d\n",(ans+mod)%mod);
}
[AGC035F] Two Histograms
可以发现一个网格对应多个方案,当且仅当存在,这时让也可以得到同样的网格。
所以我们设为不合法,容斥求出所有的合法方案即为本质不同的网格方案(即我们只在所有可以有多个方案的网格只计算这一种情况。)。
#include<bits/stdc++.h>
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
int n,m;
int Pow(int b,LL k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod;return r; }
int main(){
scanf("%d%d",&n,&m);
int Cn=1,Cm=1,ans=0,fc=1;
rep(i,0,min(n,m)){
ans = (ans + 1ll * Cn * Cm % mod * (i&1 ? -1ll : 1ll) * fc % mod * Pow(m+1,n-i) % mod * Pow(n+1,m-i)) % mod;
Cn = 1ll * Cn * (n-i) % mod * Pow(i+1 , mod-2) % mod;
Cm = 1ll * Cm * (m-i) % mod * Pow(i+1 , mod-2) % mod;
fc = 1ll * fc * (i+1) % mod;
}
printf("%d\n",(ans+mod)%mod);
}
LOJ #6181. 某个套路求和题
在质数处为,有平方因子数处为,其他时候为。
所以答案等于
前者直接算,后者。
写了一个很麻烦的实现:
#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
LL n,sn,a[maxn],s[maxn],mu[maxn];
double inv[maxn];
int cnt;
int ID(LL a){ return a <= sn ? a : cnt - n/a + 1; }
int main(){
scanf("%lld",&n);
sn = sqrt(n);
for(LL i=1;i<=n;i++) a[++cnt] = (i = n / (n / i)) , s[cnt] = i - 1;
for(LL i=2;i<=sn;i++){
inv[i] = 1.0 / i;
if(s[i] ^ s[i-1]) for(LL j=cnt,LIM=i*i;a[j]>=LIM;j--)
s[j] -= s[ID((LL)(a[j] * inv[i] + 1e-9))] - s[i-1];
}
for(int i=1;i<=cnt;i++) mu[i] = -s[i];
for(LL i=sn;i>=2;i--) if(s[i] ^ s[i-1]) for(LL j=cnt,LIM=i*i;a[j]>=LIM;j--)
mu[j] -= mu[ID((LL)(a[j] * inv[i] + 1e-9))] + s[i];
for(int i=sn;i>=1;i--) mu[i] -= mu[i-1];
mu[1] = 1;
LL ans = - 2 * s[cnt];
for(int i=1;i<=sn;i++) ans += mu[i] * (n / i / i);
printf("%lld\n",(ans%mod+mod)%mod);
}
#528. 「LibreOJ β Round #4」求和
考虑,这是个积性函数,我们考虑质数的次方处的值,可以发现这个函数在处都为。
而在处为,所以其中。
所以预处理的的前缀和即可整除优化。
#include<bits/stdc++.h>
#define maxn 5000007
#define LL long long
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
LL n,m;
int mu[maxn],pr[maxn],vis[maxn],cnt_pr;
int main(){
scanf("%lld%lld",&n,&m);
if(n > m) swap(n,m);
mu[1]=1;
int sn = sqrt(n);
rep(i,2,sn){
if(!vis[i]) pr[cnt_pr++] = i , mu[i] = -1;
for(int j=0;pr[j] * i <= sn;j++){
vis[i * pr[j]] = 1;
if(i % pr[j] == 0){
mu[i * pr[j]] = 0;
break;
}
mu[i * pr[j]] = -mu[i];
}
mu[i] += mu[i-1];
}
int ans = 0;
for(LL i=1,nxt;i<=n;i=nxt+1){
nxt = min(n / (n / i) , m / (m / i));
ans = (ans + 1ll * (n / i) % mod * (m / i % mod) % mod * (mu[(int)(sqrt(nxt)+1e-9)] - mu[(int)(sqrt(i-1)+1e-9)])) % mod;
}
printf("%d\n",(ans+mod)%mod);
}
LOJ #6244. 七选五
考虑一个固定的排列,大小为,现在你要选出的排列,求这个排列和中恰好有相等的方案数。
广义容斥原理模板题:
广义容斥原理:
设为钦定个条件满足的方案数。
则恰好有个方案满足的方案数
容斥系数为是因为
所以对于这题答案
#include<bits/stdc++.h>
#define maxn 1000007
#define LL long long
#define mod 1000000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,k,x;
int fac[maxn],inv[maxn],invf[maxn];
int C(int n,int m){ return fac[n] * 1ll * invf[m] % mod * invf[n-m] % mod; }
int main(){
fac[0] = fac[1] = inv[0] = inv[1]= invf[0] = invf[1] = 1;
scanf("%d%d%d",&n,&k,&x);
rep(i,2,n) fac[i] = 1ll * fac[i-1] *i % mod, inv[i] = 1ll * (mod - mod /i) * inv[mod % i] % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod;
int ans = 0;
rep(i,x,k)
ans = (ans + (i-x&1?-1ll:1ll)*C(i,x)%mod*C(k,i)%mod*fac[n-i]%mod*invf[n-k])%mod;
printf("%d\n",(ans+mod)%mod);
}
[ARC101C] Ribbons on Tree
设表示以为根的子树内剩下个点未匹配的容斥系数和。
则每次可以强行让到父亲的边不被涂色也就是强行匹配剩下的所有点,并乘上一个的容斥系数。
#include<bits/stdc++.h>
#define maxn 5005
#define LL long long
#define iv2 500000004
#define mod 1000000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,f[maxn][maxn],sz[maxn],fac[maxn],pw[maxn],invf[maxn],inv[maxn];
vector<int>G[maxn];
int C2(int x){ return 1ll * x * (x-1) / 2 % mod; }
void dfs(int u,int ff){
int v;f[u][1] = sz[u] = 1;
rep(i,0,G[u].size()-1) if((v=G[u][i])^ff){
dfs(v,u);
static int g[maxn]={};
rep(j,0,sz[u]+sz[v]) g[j] = 0;
rep(j,0,sz[u]) rep(k,0,sz[v]) g[j+k] = (g[j+k] + 1ll * f[u][j] * f[v][k]) % mod;
rep(j,0,sz[u]+sz[v]) f[u][j] = g[j];
sz[u] += sz[v];
}
rep(j,1,sz[u]) if(j%2==0)
f[u][0] = (f[u][0] + f[u][j] * 1ll * fac[j] % mod * pw[j/2] % mod * invf[j/2]) % mod;
if(ff) f[u][0] = -f[u][0];
}
int main(){
scanf("%d",&n);
rep(i,1,n-1){
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v),G[v].push_back(u);
}
fac[0] = fac[1] = pw[0] = 1 , pw[1] = iv2;
inv[0] = inv[1] = invf[0] = invf[1] = 1;
rep(i,2,n) fac[i] = 1ll * fac[i-1] * i % mod , pw[i] = 1ll * iv2 * pw[i-1] % mod,inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod;
dfs(1,0);
printf("%d\n",f[1][0]);
}
LOJ 「美团 CodeM 初赛 Round A」二分图染色
以讹传讹真离谱
将问题转化为棋盘模型,则是同颜色的棋子不能在同一行或同一列,不同颜色的棋子不能在同一个格子里。
设为在的棋盘内放置一种颜色的棋子的方案数。
则不同颜色的棋子不能在同一个格子里这个条件可以得到容斥一下
对于有递推式
这部分的证明我看到的网上的题解都是错的。
我们可以在选择一列,然后在行列(下面简称)放置一个棋子,同时将之前中对于行列的方案作出列平移到列的操作,这样我们就得到了一个合法方案,同样我们也可以在选择一行然后在等做类似的操作,也可以直接在放置一个棋子,同时也可以不放,一共种操作。
但是发现选择一列和选择一行这两种操作构造出来的棋盘可能会重,如果重了一定是因为行上有一个棋子列上也有一个棋子,并且这两个棋子都不在,所以我们就求出行上有一个棋子,列上有一个棋子的方案数也即,然后减去即可。
#include<bits/stdc++.h>
#define mod 1000000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define maxn 10000007
using namespace std;
int n,inv[maxn],f[maxn];
int main(){
scanf("%d",&n);
inv[0] = inv[1] = f[0] = 1 , f[1] = 2;
rep(i,2,n) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod ,
f[i] = (2ll * i * f[i-1] - (i-1ll) * (i-1ll) % mod * f[i-2]) % mod;
int ans = 0 , C = 1;
rep(i,0,n)
ans = (ans + (i&1?-1ll:1ll) * C * f[n-i] % mod * f[n-i]) % mod,
C = 1ll * C * (n-i) % mod * (n-i) % mod * inv[i+1] % mod;
printf("%d\n",(ans+mod)%mod);
}
LOJ #572. 「LibreOJ Round #11」Misaka Network 与求和
求。
令
因为的定义是次大质因子的次方,次大质因子一定是中我们筛到的质因子(不像什么次小质因子筛过程中筛不到需要预处理恶心人,比如51nod 1847 奇怪的数学题),所以可以直接筛,后面就是一个的前缀和,具体来说他等于
用递推版的成功水到。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,K,sn,cnt,s[maxn],s1[maxn],a[maxn],g[maxn],phi[maxn];
int ID(int a){ return a <= sn ? a : cnt - n/a + 1; }
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=b*b)if(k&1)r=r*b;return r; }
double inv[maxn];
int main(){
scanf("%d%d",&n,&K);
sn = sqrt(n);
for(int i=1;i<=n;i++) a[++cnt] = (i = (n / (n / i))) ,
s[cnt] = i - 1 , s1[cnt] = 1ll * i * (i+1) / 2 - 1;
for(int i=2;i<=sn;i++){
inv[i] = 1.0 / i;
if(s[i] ^ s[i-1]) for(int j=cnt,LIM=i*i;a[j]>=LIM;j--){
int t = ID((int)(a[j] * inv[i] + 1e-9));
s[j] -= s[t] - s[i-1],
s1[j] -= (s1[t] - s1[i-1]) * i;
}
}
for(int i=1;i<=cnt;i++) phi[i] = s1[i] - s[i];
for(int i=sn;i>=1;i--) if(s[i] ^ s[i-1]){
int pw = Pow(i,K);
for(int j=cnt,LIM=i*i;a[j]>=LIM;j--){
double iv = inv[i];
for(int k=i,f=i-1;1ll*k*i <= a[j];k*=i,f*=i,iv/=i){
int t = ID((int)(a[j] * iv + 1e-9));
g[j] += pw * (s[t] - s[i-1]) + g[t];
phi[j] += f * (phi[t] - s1[i] + s[i] + i);
}
}
}
for(int i=1;i<=cnt;i++) phi[i] ++ , g[i] += s[i];
int ans = 0;
for(int i=1,nxt;i<=n;i=nxt+1){
nxt = n/(n/i);
ans += (g[ID(nxt)] - g[ID(i-1)]) * (2 * (phi[ID(n/i)] - 1) + 1);
}
unsigned int ret = ans;
printf("%u\n",ret);
}