数据结构
这题的修改是强行加进去迷惑你的。
考虑怎么求树的带权重心。
结论:设 u u u的子树和为 s z u sz_u szu,所有点权值和为 s s s,那么树的带权重心等价于,满足 s z u ≥ ⌈ s 2 ⌉ sz_u\ge \lceil\frac{s}{2}\rceil szu≥⌈2s⌉且深度最大的点 u u u。
道理很简单。显然这个条件是必要的。其次, u u u肯定在根节点所在的重链上,并且满足条件的点一定是重链的一段前缀,容易发现只有这一段前缀的结尾那个点能作为树的重心。
下面的分析就很简单了。
第一种方法,我们只要能确定一个点一定在重心的子树内,然后从这个点往上跳即可。下一步比较构造,考虑在 dfn \text{dfn} dfn序中找到第一个满足前缀和 ≥ ⌈ s 2 ⌉ \ge \lceil\frac{s}{2}\rceil ≥⌈2s⌉的点,这个点一定在重心的子树对应的那段 dfn \text{dfn} dfn序上,换句话说一定在重心的子树内,往上跳即可。注意树的重心可能有两个,因此 u u u的父亲也可能是重心,所以当子树和恰好是 s 2 \frac{s}{2} 2s时要返回 u u u的父亲。又因为点权可能为零,因此最后要先跳一段 0 0 0再返回父亲。
第二种方法,记录上一次重心的位置,如果是对链操作,那么新的重心一定在链端点的祖先上,如果是对子树操作,那么新的重心可能在 u u u的祖先上,也可能在 u u u的重链上,直接在重链上跳即可。
复杂度 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=3e5+5;
int n,Q,f[N][20],dfn[N],dep[N],tp[N],sz[N],son[N],rk[N],num;
ll sum;
vector<int>g[N];
struct node{
ll sum,dat;
}t[N<<2];
void add(int p,int l,int r,ll x){
t[p].sum+=(r-l+1)*x,t[p].dat+=x;
}
void pushdown(int p,int l,int r){
int mid=l+r>>1;
if(t[p].dat){
add(p<<1,l,mid,t[p].dat),add(p<<1|1,mid+1,r,t[p].dat),t[p].dat=0;
}
}
void pushup(int p){
t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
}
void upd(int p,int l,int r,int ql,int qr,ll x){
if(ql<=l&&r<=qr){
add(p,l,r,x);return;
}int mid=l+r>>1;pushdown(p,l,r);
if(ql<=mid)upd(p<<1,l,mid,ql,qr,x);
if(mid<qr)upd(p<<1|1,mid+1,r,ql,qr,x);
pushup(p);
}
ll qry(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return t[p].sum;
int mid=l+r>>1;pushdown(p,l,r);
if(qr<=mid)return qry(p<<1,l,mid,ql,qr);
if(mid<ql)return qry(p<<1|1,mid+1,r,ql,qr);
return qry(p<<1,l,mid,ql,qr)+qry(p<<1|1,mid+1,r,ql,qr);
}
void dfs(int u,int topf){
sz[u]=1,f[u][0]=topf,dep[u]=dep[topf]+1;
for(int i=1;i<20;i++)f[u][i]=f[f[u][i-1]][i-1];
for(auto v:g[u]){
if(v!=topf){
dfs(v,u),sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
}
void dfs2(int u,int topf){
tp[u]=topf,dep[u]=dep[topf]+1,dfn[u]=++num,rk[num]=u;
if(son[u])dfs2(son[u],topf);
for(auto v:g[u]){
if(!dfn[v])dfs2(v,v);
}
}
int Lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
int query(){
int l=1,r=n,res=0;sum=t[1].sum;
while(l<=r){
int mid=l+r>>1;
if(qry(1,1,n,1,mid)>=(sum+1)/2)res=mid,r=mid-1;
else l=mid+1;
}res=rk[res];
if(qry(1,1,n,dfn[res],dfn[res]+sz[res]-1)<(sum+1)/2){
for(int i=19;i>=0;i--){
int u=f[res][i];
if(u&&qry(1,1,n,dfn[u],dfn[u]+sz[u]-1)<(sum+1)/2)res=u;
}res=f[res][0];
}
if(qry(1,1,n,dfn[res],dfn[res]+sz[res]-1)==sum/2){
for(int i=19;i>=0;i--){
int u=f[res][i];
if(u&&qry(1,1,n,dfn[u],dfn[u]+sz[u]-1)==sum/2)res=u;
}if(f[res][0])return f[res][0];
return res;
}return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v,g[u].pb(v),g[v].pb(u);
}cin>>Q;dfs(1,0),dfs2(1,1);
for(int i=1;i<=Q;i++){
int op,x,y,w;
cin>>op>>x>>y;
if(op==1){
upd(1,1,n,dfn[x],dfn[x]+sz[x]-1,y);
}
else{
cin>>w;
int fx=tp[x],fy=tp[y];
while(fx!=fy){
if(dep[fx]>dep[fy])upd(1,1,n,dfn[fx],dfn[x],w),x=f[fx][0];
else upd(1,1,n,dfn[fy],dfn[y],w),y=f[fy][0];
fx=tp[x],fy=tp[y];
}if(dfn[x]>dfn[y])swap(x,y);
upd(1,1,n,dfn[x],dfn[y],w);
}
cout<<query()<<"\n";
}
}
签到
把容斥的式子写出来: ( − 1 ) ∣ S ∣ ( n + m + ( c − 1 ) ∣ S ∣ − ∑ i ∈ S b i m ) (-1)^{|S|}\binom{n+m+(c-1)|S|-\sum_{i\in S}{b^i}}{m} (−1)∣S∣(mn+m+(c−1)∣S∣−∑i∈Sbi)
然而直接组合数非常难算。但是注意到 m m m很小,因此我们可以把组合数看成一个多项式。
刚开始想的是一些比较特殊的情况,可以简单递推求出,但是发现对于普通的情况难以处理,感觉数位 d p dp dp部分又比较麻烦于是就一无所获了。
首先把 n n n转化成 b b b进制,然后枚举 ∣ S ∣ |S| ∣S∣。 注意到 ∑ i ∈ S b i \sum_{i\in S}b^i ∑i∈Sbi的数位上都是 1 1 1,因此我们枚举一段前缀就不用考虑负数的情况。问题转化为,从 1 , 2 , . . . , m 1,2,...,m 1,2,...,m中选 k k k个数,记作集合 T T T,对于每个 i ∈ [ 1 , m ] i\in [1,m] i∈[1,m],求每种情况下 ( ∑ j ∈ T b j ) i (\sum_{j\in T}b_j)^i (∑j∈Tbj)i的和。最简单的想法是,每次加入一个数时,用二项式定理暴力展开。这可以用 O ( n 4 ) O(n^4) O(n4)的 d p dp dp预处理求出。
复杂度 O ( n 4 ) O(n^4) O(n4)。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int mod=998244353;
ll B,C,m;
ll fac[100],dp[100][100][100],inv[100],Pw[10000],res,a[105];
string s;
struct bignum{
int len;
ll a[10000];
bignum(){memset(a,0,sizeof a);}
friend bool operator <(bignum a,bignum b){
if(a.len<b.len)return 1;
if(a.len>b.len)return 0;
for(int i=a.len;i>=0;i--){
if(a.a[i]!=b.a[i])return a.a[i]<b.a[i];
}return 0;
}
friend bignum operator -(bignum a,bignum b){
assert(a.len>=b.len);
bignum c;c.len=a.len;
for(int i=0;i<=a.len;i++){
if(a.a[i]<b.a[i])a.a[i]+=B,a.a[i+1]--;
c.a[i]=a.a[i]-b.a[i];
}while(c.len>=0&&!c.a[c.len])c.len--;
return c;
}
friend bignum operator +(bignum a,bignum b){
a.len=max(a.len,b.len);
for(int i=0;i<=a.len;i++){
a.a[i]+=b.a[i];
if(a.a[i]>=B)a.a[i]-=B,a.a[i+1]++;
}while(a.a[a.len+1])a.len++;
return a;
}
friend bignum operator /(bignum a,ll b){
ll c=0;
for(int i=a.len;i>=0;i--){
c=c*10+a.a[i];a.a[i]=c/b,c%=b;
} while(a.len>=0&&!a.a[a.len])a.len--;
return a;
}
friend ll operator %(bignum a,ll b){
ll c=0;for(int i=a.len;i>=0;i--)c=(c*10+a.a[i])%b;
return c;
}
}n,n3,n2;
bignum change(ll x){
bignum a;a.len=-1;
while(x){
a.a[++a.len]=x%B,x/=B;
}return a;
}
bignum Change(bignum x){
bignum a;a.len=-1;
while(x.len>=0){
a.a[++a.len]=x%B,x=x/B;
}return a;
}
struct poly{
ll a[100];
poly(){memset(a,0,sizeof a);}
friend poly operator *(poly a,poly b){
poly c;
for(int i=0;i<=m;i++){
for(int j=0;j<=m;j++){
c.a[i+j]=(c.a[i+j]+a.a[i]*b.a[j])%mod;
}
}return c;
}
friend poly operator +(poly a,poly b){
for(int i=0;i<=m;i++)a.a[i]=(a.a[i]+b.a[i])%mod;
return a;
}
friend poly operator *(poly a,ll b){
for(int i=0;i<=m;i++)a.a[i]=a.a[i]*b%mod;
return a;
}
}f;
ll pw(ll x,ll y=mod-2){
ll z(1);
for(;y;y>>=1){
if(y&1)z=z*x%mod;
x=x*x%mod;
}return z;
}
void init(int n){
fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
inv[n]=pw(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
ll binom(int x,int y){
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void dfs(int x,int y,ll z,int l){
if(x==0){
if(y==0){
for(int i=0;i<=m;i++)a[i]=(a[i]+pw(z,i))%mod;
}
return;
}
if(!l){
for(int i=0;i<=m;i++){
for(int j=0;j<=i;j++){
a[i]=(a[i]+binom(i,j)*dp[x][y][j]%mod*pw(z,i-j))%mod;
}
}
return;
}if(n3.a[x]>1)dfs(x,y,z,0);
else if(n3.a[x]==0)dfs(x-1,y,z,1);
else {
dfs(x-1,y,z,0);
if(y)dfs(x-1,y-1,(z+Pw[x])%mod,1);
}
}
int main(){
cin>>m>>B>>C,init(m);cin>>s;Pw[0]=1;for(int i=1;i<=m*m;i++)Pw[i]=Pw[i-1]*B%mod;
reverse(s.begin(),s.end()),n.len=s.size()-1;
for(int i=0;i<=n.len;i++)n.a[i]=s[i]-'0';ll nn=n%mod;
n=Change(n);dp[0][0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=m;j++){
for(int k=0;k<=m;k++){
dp[i][j][k]=dp[i-1][j][k];
if(j){
for(int l=0;l<=k;l++){
dp[i][j][k]=(dp[i][j][k]+binom(k,l)*dp[i-1][j-1][l]%mod*Pw[i*(k-l)])%mod;
}
}
}
}
}
for(int i=0;i<=m;i++){
n2=change((C-1)*i);
if(C>=1){
n3=n+change(m-1)+n2;
}
else{
n3=n+change(m-1),n2=change((1-C)*i);
if(n3<n2)continue;
n3=n3-n2;
}
memset(a,0,sizeof a),memset(f.a,0,sizeof f.a),f.a[0]=1,dfs(n3.len,i,0,1);
for(int j=0;j<m;j++){
poly g;g.a[1]=-1,g.a[0]=(nn+m+(C-1)*i-1-j)%mod;
f=f*g;
}for(int j=0;j<=m;j++)f.a[j]=f.a[j]*inv[m]%mod;
for(int j=0;j<=m;j++){
if(i&1)res=(res-f.a[j]*a[j])%mod;
else res=(res+f.a[j]*a[j])%mod;
}
}
cout<<(res+mod)%mod;
}
爆搜
不会。只会 O ( 2 n poly ( n ) ) O(2^n\text{poly}(n)) O(2npoly(n))的做法。