神仙场
T2全场最高17分
坐看大佬全员200
T1 Union
答案对
1
0
9
+
7
10^9+7
109+7取模。
n
≤
20
,
C
i
,
j
<
1
0
9
+
7
n\leq 20,C_{i,j}<10^9+7
n≤20,Ci,j<109+7
设 f ( s ) f(s) f(s)表示点集 s s s所构成无向连通图的方案数, g ( s ) g(s) g(s)表示点集 s s s所构成的所有图的方案数(可不连通)。
则 g ( s ) = ∏ u , v ∈ s , u ≠ v ( C u , v + 1 ) g(s)=\prod\limits_{u,v\in s,u\neq v}(C_{u,v}+1) g(s)=u,v∈s,u̸=v∏(Cu,v+1)
考虑枚举 s s s中标号最小的点 x x x的连通块集合 t t t:
g ( s ) = ∑ x ∈ t , t ⊆ s f ( t ) g ( s − t ) g(s)=\sum\limits_{x\in t,t\subseteq s}f(t)g(s-t) g(s)=x∈t,t⊆s∑f(t)g(s−t)
设全集为 a l l all all, g ′ ( s ) = g ( s ) × [ 1 ∉ s ] g'(s)=g(s)\times[1\notin s] g′(s)=g(s)×[1∈/s],则 g ( a l l ) = ∑ t ⊆ a l l f ( t ) g ′ ( a l l − t ) g(all)=\sum\limits_{t\subseteq all}f(t)g'(all-t) g(all)=t⊆all∑f(t)g′(all−t),在子集卷积意义下,即为 g = f ∗ g ′ → f = g ∗ ( g ′ ) − 1 g=f*g'\to f=g*(g')^{-1} g=f∗g′→f=g∗(g′)−1。做一个子集卷积意义下的除法即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=(1<<20)+10,mod=1e9+7;
int n,S,sz[N],h[N],v[N],id[N],tmp[21],inv[21];
int f[21][N],g[21][N],c[21][21];
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void fmt(int *e,int pr)
{
for(int j,i=1;i<S;i<<=1)
for(j=1;j<S;++j) if(j&i)
pr?ad(e[j],e[j^i]):dc(e[j],e[j^i]);
}
int main(){
freopen("union.in","r",stdin);
freopen("union.out","w",stdout);
int i,j,k;
scanf("%d",&n);S=1<<n;
for(i=0;i<n;++i)
for(j=i+1;j<n;++j){
scanf("%d",&c[i][j]);
c[j][i]=c[i][j];
}
for(i=0;i<n;++i) id[1<<i]=i;
f[0][0]=g[0][0]=v[0]=1;
for(i=1;i<S;++i){
sz[i]=sz[i>>1]+(i&1);j=id[i&(-i)];
v[i]=v[i^(i&(-i))];
for(k=0;k<n;++k) if((j^k)&&((i>>k)&1))
v[i]=(ll)v[i]*(c[j][k]+1)%mod;
f[sz[i]][i]=v[i];
if(!(i&1)) g[sz[i]][i]=v[i];
}
for(i=0;i<=n;++i) fmt(f[i],1),fmt(g[i],1);
for(i=0;i<S;++i){
memset(tmp,0,sizeof(tmp));tmp[0]=1;
for(j=0;j<=n;++j){
inv[j]=tmp[j];
for(k=1;j+k<=n;++k)
dc(tmp[j+k],(ll)inv[j]*g[k][i]%mod);
}
for(j=0;j<=n;++j) g[j][i]=inv[j];
}
for(j=0;j<=n;++j)
for(i=0;i<S;++i)
ad(h[i],(ll)f[j][i]*g[n-j][i]%mod);
fmt(h,0);
printf("%d",h[S-1]);
return 0;
}
T2 Power
n
≤
50
,
1
≤
k
≤
1
0
6
,
∣
a
∣
≤
1
0
5
,
−
2000
≤
l
i
≤
r
i
≤
2000
n\leq 50,1\leq k\leq10^6,|a|\leq 10^5,-2000\leq l_i\leq r_i\leq 2000
n≤50,1≤k≤106,∣a∣≤105,−2000≤li≤ri≤2000
神仙题,实现也很神仙。
首先考虑 k k k的奇偶性,按 ∑ x i \sum x_i ∑xi的值域分段讨论:
- k k k为奇,考虑 ( − ∞ , a ] , ( a , + ∞ ) (-\infty,a],(a,+\infty) (−∞,a],(a,+∞)。
- k k k为偶,考虑 ( − ∞ , − ∣ a ∣ ] , ( − ∣ a ∣ , ∣ a ∣ ] , ( ∣ a ∣ , + ∞ ) (-\infty,-|a|],(-|a|,|a|],(|a|,+\infty) (−∞,−∣a∣],(−∣a∣,∣a∣],(∣a∣,+∞)
需要求解的是 ∑ x i \sum x_i ∑xi落在某个区间的概率,假设所有变量是在 [ 0 , + ∞ ) [0,+\infty) [0,+∞)上均匀随机的( [ l i , r i ] [l_i,r_i] [li,ri]的限制容斥一下即可)。
设
p
n
(
s
)
p_n(s)
pn(s)表示
n
n
n个随机变量之和不超过
s
s
s的概率(更准确来说时
∑
x
i
<
=
s
\sum x_i<=s
∑xi<=s的点在
n
n
n维空间中所占体积),
n
>
0
n>0
n>0时考虑枚举最后一个变量的取值:
p
n
(
x
)
=
∫
0
s
p
n
−
1
(
x
)
d
x
p_n(x)=\int_{0}^sp_{n-1}(x){\rm d}x
pn(x)=∫0spn−1(x)dx
由 p 0 = 1 p_0=1 p0=1递推得到:
p n = x n n ! p_n=\dfrac{x^n}{n!} pn=n!xn。
那么 n n n个随机变量之和恰好等于 s s s的概率 q n ( x ) q_n(x) qn(x)即为 p n − 1 ( x ) p_{n-1}(x) pn−1(x)。
考虑
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]的容斥,每个随机变量用
s
i
+
x
s_i+x
si+x的形式表示,设其中
s
i
=
l
i
s_i=l_i
si=li,
则区间
∑
x
i
=
[
l
,
r
]
\sum x_i=[l,r]
∑xi=[l,r]的贡献为:
(1) ∫ l r q n ( x − ∑ s i ) m a x ( x k , a k ) d x \int _{l}^rq_n(x-\sum s_i)max(x^k,a^k){\rm d}x\tag 1 ∫lrqn(x−∑si)max(xk,ak)dx(1)。
注意对于上式,求上界为
+
∞
+\infty
+∞的积分会出问题,考虑转而将每个随机变量用
s
i
−
x
s_i-x
si−x的形式表示,设其中
s
i
=
l
i
s_i=l_i
si=li,则区间
∑
x
i
=
[
l
,
r
]
\sum x_i=[l,r]
∑xi=[l,r]的贡献为:
(2)
∫
l
r
q
n
(
∑
s
i
−
x
)
m
a
x
(
x
k
,
a
k
)
d
x
\int _{l}^r q_n(\sum s_i-x)max(x^k,a^k){\rm d}x\tag 2
∫lrqn(∑si−x)max(xk,ak)dx(2)
积分二项式定理展开后得到:
- x ≤ a x\leq a x≤a, a k ( p n ( r − ∑ s i ) − p n ( l − ∑ s i ) ) a^k(p_n(r-\sum s_i)-p_n(l-\sum s_i)) ak(pn(r−∑si)−pn(l−∑si))
- x > a x>a x>a, 1 ( n − 1 ) ! ∑ i = 0 n − 1 ( − 1 ) i ( n − 1 i ) x i + k + 1 ( ∑ s i ) n − i − 1 1 i + k + 1 \frac{1}{(n-1)!}\sum \limits_{i=0}^{n-1}(-1)^i\binom{n-1}{i}x^{i+k+1}(\sum s_i)^{n-i-1}\frac{1}{i+k+1} (n−1)!1i=0∑n−1(−1)i(in−1)xi+k+1(∑si)n−i−1i+k+11
用 ( 1 ) (1) (1)式求 [ ∑ s i , a ] [\sum s_i,a] [∑si,a]区间的答案,用 ( 2 ) (2) (2)式求 [ a i , ∑ s i ] [a_i,\sum s_i] [ai,∑si]的答案,就解决了上下界问题。
容斥的过程相当于,将其中某些 s i s_i si设为 r i r_i ri来保证不合法。所以可以将初始的 S S S设为 ∑ l i \sum l_i ∑li,每改变一个 s i s_i si相当于将 S + = l e n i S+=len_i S+=leni。而 ∑ l e n i \sum len_i ∑leni在题中已保证 ≤ 4000 × 50 \leq 4000\times 50 ≤4000×50,做一个 ∑ l e n i \sum len_i ∑leni大小的 01 01 01背包来求出 S + x S+x S+x时的系数。初始化 S + 0 S+0 S+0的系数为1。
需要特判所有 l i = r i l_i=r_i li=ri的情况。
最后答案除以 ∏ i = 1 , l i ≠ r i n ( r i − l i ) \prod \limits_{i=1,l_i\neq r_i}^n(r_i-l_i) i=1,li̸=ri∏n(ri−li)即可。
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
typedef long long ll;
int n,K,a,sum,frc[55],nv[55],f[200005],sl,sr,s,ans;
struct le{int l,r;}p[55];
inline int fp(int x,int y)
{
int re=1;x%=mod;if(x<0) x+=mod;
for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) re=(ll)re*x%mod;
return re;
}
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int dci(int x,int y){x-=y;return x<0?x+mod:x;}
inline int binom(int n,int m){
if(m>n) return 0;if(n==m || m==0) return 1;
return (ll)frc[n]*nv[m]%mod*(ll)nv[n-m]%mod;
}
inline int cal(int s,int x)
{
int i,j,re=0;x%=mod;if(x<0) x+=mod;
for(i=0;i<n;++i){
j=(ll)binom(n-1,i)*fp(x,i+K+1)%mod*(ll)fp(s,n-1-i)%mod*(ll)fp(i+K+1,mod-2)%mod;
(i&1)?dc(re,j):ad(re,j);
}
return (ll)re*nv[n-1]%mod;
}
int main(){
int i,j,k;frc[0]=frc[1]=nv[0]=nv[1]=1;
scanf("%d%d%d",&n,&K,&a);
for(i=1;i<=n;++i) {
scanf("%d%d",&p[i].l,&p[i].r);
sl+=p[i].l;sr+=p[i].r;
if(p[i].l==p[i].r) i--,n--;else sum+=(p[i].r-p[i].l);
}
if(!n) {printf("%d",(K&1)?fp(max(sl,a),K):fp(max(abs(sl),abs(a)),K));return 0;}
for(i=2;i<=n;++i)
frc[i]=(ll)frc[i-1]*i%mod,
nv[i]=(ll)(mod-mod/i)*nv[mod%i]%mod;
for(i=2;i<=n;++i) nv[i]=(ll)nv[i]*nv[i-1]%mod;
f[0]=1;
for(i=1;i<=n;++i){
k=p[i].r-p[i].l;
for(j=sum-k;j>=0;--j)
dc(f[j+k],f[j]);
}
for(i=0;i<=sum;++i) if(f[i]){
if((s=sr-i)>a) ad(ans,(ll)f[i]*dci(cal(s,s),cal(s,a))%mod);
if((s=sl+i)<a) ad(ans,(ll)f[i]*fp(a,K)%mod*(ll)fp(a-s,n)%mod*(ll)nv[n]%mod);
if(s<-a && (!(K&1))){
dc(ans,(ll)f[i]*fp(a,K)%mod*(ll)fp(-a-s,n)%mod*(ll)nv[n]%mod);
ad(ans,(ll)f[i]*dci(cal(-s,-s),cal(-s,a))%mod);
}
}
for(i=1;i<=n;++i) ans=(ll)ans*fp(p[i].r-p[i].l,mod-2);
printf("%d",ans);
return 0;
}
T3 Traffic
n
≤
1
0
5
n\leq 10^5
n≤105
对于每个点,显然是从最大的连通块中选一颗子树接到最小的连通块上,所以先 d f s dfs dfs一遍求出删去每个点后的最大,次大,最小连通块。
二分答案转成区间存在性问题,树状数组以
s
i
z
e
size
size为下标维护
d
f
s
dfs
dfs序区间内
s
i
z
e
size
size在范围内的个数。
注意根到当前点
x
x
x路径上的所有点
s
i
z
e
size
size需要
−
=
s
i
z
e
x
-=size_x
−=sizex,另用一个
B
I
T
BIT
BIT特殊判断即可。
#include<bits/stdc++.h>
#define mid (l+r>>1)
using namespace std;
const int N=1e5+10,inf=0x3f3f3f3f;
int n,bit[N],f[N],sz[N],df[N],dfn,L,R;
int head[N],to[N],nxt[N],tot,ans[N];
vector<int>tre[N];
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
struct P{
int mn,mx,id,smx;
P(){mn=inf;mx=smx=-inf;}
inline void ins(int u,int v)
{
mn=min(mn,u);
if(u>=mx) {smx=mx;mx=u;id=v;}
else if(u>smx) smx=u;
}
}p[N];
void dfs(int x)
{
sz[x]=1;df[x]=++dfn;
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];dfs(j);sz[x]+=sz[j];
p[x].ins(sz[j],j);
}
if(x>1) p[x].ins(n-sz[x],f[x]);
}
inline void modify(int x,int v)
{for(;x<=n;x+=(x&(-x))) bit[x]+=v;}
inline int fd(int x)
{return upper_bound(tre[x].begin(),tre[x].end(),R)-lower_bound(tre[x].begin(),tre[x].end(),L);}
void cal(int x)
{
int i,j;ans[x]=p[x].mx;
if(p[x].mx>p[x].smx && head[x]){
int sl,sr,l=max(p[x].smx,(p[x].mx+p[x].mn-1)/2+1),r=p[x].mx-1;
for(;l<=r;){
j=0;sl=p[x].mx-mid-1;sr=mid-p[x].mn;
if(p[x].id==f[x]){
L=df[x],R=L+sz[x]-1;
for(i=sr;i>0;i-=(i&(-i))) j+=(tre[i].size()-fd(i)-bit[i]);
for(i=sl;i>0;i-=(i&(-i))) j-=(tre[i].size()-fd(i)-bit[i]);
for(i=min(sr+sz[x],n);i>0;i-=(i&(-i))) j+=bit[i];
for(i=min(sl+sz[x],n);i>0;i-=(i&(-i))) j-=bit[i];
}else{
L=df[p[x].id];R=L+sz[p[x].id]-1;
for(i=sr;i>0;i-=(i&(-i))) j+=fd(i);
for(i=sl;i>0;i-=(i&(-i))) j-=fd(i);
}
j>0?(r=(ans[x]=mid)-1):(l=mid+1);
}
}
modify(sz[x],1);for(i=head[x];i;i=nxt[i]) cal(to[i]);modify(sz[x],-1);
}
int main(){
int i,j;
rd(n);if(n==1) {puts("0");return 0;}
for(i=2;i<=n;++i){rd(f[i]);lk(f[i],i);}
dfs(1);
for(i=1;i<=n;++i)
for(j=sz[i];j<=n;j+=(j&(-j)))
tre[j].push_back(df[i]);
for(i=1;i<=n;++i) sort(tre[i].begin(),tre[i].end());
cal(1);
for(i=1;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}
小结
T1的子集枚举没想出来不应该啊。
T2忘了怎么求
x
n
x^n
xn的积分是真的zz。
T3没有做出来不可原谅。