【考试总结】2022-03-20
签到
枚举哪些位置超出了限制,注意这里需要将 减 ,那么可以直接列出答案的计算表达式
如果我们可以保证 ,那么组合数是关于 的多项式
枚举 以及 和 的 ( 进制)及后面一位的数字
这里想要得到答案,还需要在低位求出关于 的多项式在所有 中的每个次幂的和
用 来做:设 表示考虑了 这些数字并选出了 个 并对第 次幂的自变量求和,转移使用二项式定理合并即可
注意需要写高精度除法,但是进制转换只用做一次,那么使用模拟竖式的方法是可行的
Code Display
int Bas=10;
struct node{
vector<int> a;
inline int size(){return a.size();}
inline void adjust(){
int siz=a.size();
for(int i=0;i<siz-1;++i) a[i+1]+=a[i]/Bas,a[i]%=Bas;
while(a[siz-1]>=Bas){
a.emplace_back(a[siz-1]/Bas);
a[siz-1]%=Bas;
++siz;
}
while(siz>1&&!a[siz-1]) a.pop_back(),--siz;
return ;
}
inline int toint(int Mod=mod){
int siz=a.size(),res=0;
for(int i=siz-1;i>=0;--i) res=(res*Bas+a[i])%Mod;
return res;
}
inline void init(string x){
vector<int> num[2]; num[0].resize(x.size());
int cur=0;
int len=x.size();
for(int i=len-1;i>=0;--i) num[cur][i]=x[i]-'0';
reverse(num[cur].begin(),num[cur].end());
while(num[cur].size()>1||num[cur][0]>0){
num[cur^1].clear();
int tmp=0,siz=num[cur].size();
bool flag=0;
for(int i=siz-1;i>=0;--i){
tmp=tmp*10+num[cur][i];
if(tmp>=Bas) flag=1;
if(flag) num[cur^1].emplace_back(tmp/Bas),tmp%=Bas;
}
a.emplace_back(tmp);
cur^=1;
reverse(num[cur].begin(),num[cur].end());
siz=num[cur].size(); num[cur].emplace_back(0);
for(int i=0;i<siz;++i){
num[cur][i+1]+=num[cur][i]/10;
num[cur][i]%=10;
}
while(num[cur][siz]) num[cur].emplace_back(0),++siz,num[cur][siz]+=num[cur][siz-1]/10,num[cur][siz-1]%=10;
num[cur].pop_back();
}
return ;
}
inline void init(int x){
a.clear();
do{a.push_back(x%Bas),x/=Bas;}while(x);
return ;
}
bool operator <(node b)const{
if(a.size()!=b.size()) return a.size()<b.size();
int len=a.size();
for(int i=len-1;~i;--i) if(a[i]^b.a[i]) return a[i]<b.a[i];
return 0;
}
bool operator ==(node b)const{
if(b.size()!=a.size()) return 0;
int len=a.size();
for(int i=0;i<len;++i) if(a[i]!=b.a[i]) return 0;
return 1;
}
bool operator <=(node &b)const{return *this<b||*this==b;}
node operator +(node b)const{
node res;
int s1=a.size(),s2=b.a.size(); res.a.resize(max(s1,s2));
for(int i=0;i<s1;++i) res.a[i]+=a[i];
for(int i=0;i<s2;++i) res.a[i]+=b.a[i];
res.adjust();
return res;
}
node operator -(node b)const{
node res=*this;
int s1=a.size(),s2=b.size();
for(int i=0;i<s2;++i){
res.a[i]-=b.a[i];
if(res.a[i]<0) res.a[i]+=Bas,res.a[i+1]--;
}
for(int i=s2;i<s1;++i) if(res.a[i]<0) res.a[i+1]--,res.a[i]+=Bas;
res.adjust();
return res;
}
node operator *(node b)const{
node res; res.a.resize(a.size()+b.size()-1);
for(int i=0;i<a.size();++i) for(int j=0;j<b.size();++j) res.a[i+j]+=a[i]*b.a[j];
res.adjust();
return res;
}
bool operator !=(node &b){return !(*this==b);}
node operator *(const int &p)const{node t; t.init(p); return *this*t;}
node operator +(const int &p)const{node t; t.init(p); return *this+t;}
node operator -(const int &p)const{node t; t.init(p); return *this-t;}
inline void output(){
int siz=a.size(); printf("%lld ",a[siz-1]);
putchar(' ');
for(int i=siz-2;~i;--i) printf("%lld ",a[i]);
return ;
}
}n;
const int N=2010;
int poly[N],m,b,c,ans;
string str_n;
int pw[N][N],dp[100][100][100];
int ifac[N],C[N][N];
signed main(){
// freopen("checkin.in","r",stdin); freopen("checkin.out","w",stdout);
C[0][0]=ifac[0]=1;
for(int i=1;i<=1000;++i){
C[i][0]=1;
ifac[i]=mul(ifac[i-1],ksm(i,mod-2));
for(int j=1;j<=i;++j) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
}
m=read(); b=read(); c=read();
Bas=b;
cin>>str_n;
if(str_n=="0") puts("0"),exit(0);
n.init(str_n); n=n-1;
pw[0][0]=1; rep(i,1,2000) pw[0][i]=1;
for(int i=1;i<=2000;++i){
pw[i][0]=1;
pw[i][1]=mul(pw[i-1][1],b);
for(int j=2;j<=2000;++j) pw[i][j]=mul(pw[i][j-1],pw[i][1]);
}
dp[0][0][0]=1;
for(int k=0;k<=m;++k){
for(int i=1;i<=m;++i){
for(int j=0;j<=i;++j){
int sum=dp[i-1][j][k];
if(j) for(int t=0;t<=k;++t) ckadd(sum,mul(C[k][t],mul(dp[i-1][j-1][t],pw[i][k-t])));
dp[i][j][k]=sum;
}
}
}
for(int S=0;S<=m;++S){
node A,n_m,n_S,n_c;
n_m.init(m);
n_S.init(S);
n_c.init(abs(1-c));
if(c<0&&n+n_m<n_c*n_S) continue;
if(c<=0) A=n+n_m-n_c*n_S;
else A=n+n_m+n_c*n_S;
int a=A.toint();
int sum=0;
vector<vector<int> >tmp(m);
for(int i=0;i<m;++i) tmp[i].resize(i+2);
tmp[0][0]=a; tmp[0][1]=mod-1;
for(int i=1;i<m;++i){
for(int j=0;j<=i+1;++j){
if(j<=i) tmp[i][j]=mul(tmp[i-1][j],del(a,i));
if(j) ckdel(tmp[i][j],tmp[i-1][j-1]);
}
}
for(int i=0;i<=m;++i) poly[i]=mul(tmp[m-1][i],ifac[m]);
auto evalue=[&](int x){
int sum=0,pw=1;
for(int i=0;i<=m;++i,ckmul(pw,x)) ckadd(sum,mul(poly[i],pw));
return sum;
};
int hig=0,siz=A.size(),cnt=0;
for(int lcp=siz-1;~lcp&&cnt<=S;--lcp){
if(!lcp){
if(cnt==S) ckadd(sum,evalue(hig));
break;
}
int up=min(A.a[lcp],2ll);
for(int cur=0;cur<up;++cur){
if(cur==1) ++cnt,ckadd(hig,pw[lcp][1]);
if(cnt>S) break;
vector<int> pwh(1+m);
pwh[0]=1;
for(int i=1;i<=m;++i) pwh[i]=mul(pwh[i-1],hig);
int tmp=0;
for(int k=0;k<=m;++k){
int sumx=0;
for(int t=0;t<=k;++t) ckadd(sumx,mul(C[k][t],mul(pwh[k-t],dp[lcp-1][S-cnt][t])));
ckadd(tmp,mul(poly[k],sumx));
}
ckadd(sum,tmp);
}
if(A.a[lcp]==1) ++cnt,ckadd(hig,pw[lcp][1]);
if(A.a[lcp]>1) break;
}
if(S&1) ckdel(ans,sum); else ckadd(ans,sum);
}
print(ans); putchar('\n');
return 0;
}
数据结构
将树拍到 序上考虑,序列上的带权中点一定在带权重心的子树内,写倍增判定即可
卡常,所以用了 zkw线段树
我还有一个非常麻烦的使用 新重心在两个原来重心的路径上 的结论的方法,比这个垃圾到不知道哪里去了
Code Display
vector<int> G[N];
int n;
int fa[N],dep[N],siz[N],top[N],son[N],dfn[N],ord[N],tim;
int bz[N][20];
inline void dfs2(int x,int topf){
top[x]=topf; ord[dfn[x]=++tim]=x; if(son[x]) dfs2(son[x],topf);
for(auto t:G[x]) if(!dfn[t]) dfs2(t,t);
}
inline void dfs1(int x,int fat){
dep[x]=dep[fa[x]=fat]+(siz[x]=1);
bz[x][0]=fat;
for(int i=1;bz[x][i-1];++i) bz[x][i]=bz[bz[x][i-1]][i-1];
for(auto t:G[x]) if(t!=fat){
dfs1(t,x); siz[x]+=siz[t];
if(siz[t]>siz[son[x]]) son[x]=t;
} return ;
}
ll nec;
struct Seg{
#define ls p<<1
#define rs p<<1|1
int lim;
ll sum[N<<2],tag[N<<2];
inline void build(int n){
lim=1;
while(lim<=(n+1)) lim<<=1;
return ;
}
inline void upd(int l,int r,ll v){
l+=lim-1; r+=lim+1;
int lc=0,rc=0;
for(int len=1;l!=r-1;l>>=1,r>>=1,len<<=1){
sum[l]+=lc*v; sum[r]+=rc*v;
if(!(l&1)) sum[l^1]+=len*v,tag[l^1]+=v,lc+=len;
if(r&1) sum[r^1]+=len*v,tag[r^1]+=v,rc+=len;
}
while(l){
sum[l]+=lc*v; sum[r]+=rc*v;
l>>=1; r>>=1;
}
return ;
}
inline ll Query(int l,int r){
ll res=0,rc=0,lc=0;
l+=lim-1; r+=lim+1;
for(int len=1;l!=r-1;r>>=1,l>>=1,len<<=1){
res+=tag[l]*lc+tag[r]*rc;
if(r&1) res+=sum[r^1],rc+=len;
if(!(l&1)) res+=sum[l^1],lc+=len;
}
while(l){
res+=tag[l]*lc+tag[r]*rc;
l>>=1; r>>=1;
}
return res;
}
#undef ls
#undef rs
#undef lson
#undef rson
}T;
signed main(){
freopen("yyl.in","r",stdin); freopen("yyl.out","w",stdout);
n=read();
rep(i,1,n-1){
int u=read(),v=read();
G[u].push_back(v); G[v].emplace_back(u);
}
int Q=read();
dfs1(1,0); dfs2(1,1); T.build(n);
while(Q--){
if(read()-1){
int u=read(),v=read(),w=read();
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
T.upd(dfn[top[u]],dfn[u],w);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
T.upd(dfn[u],dfn[v],w);
}else{
int x=read(),w=read();
T.upd(dfn[x],dfn[x]+siz[x]-1,w);
}
nec=T.sum[1]/2+1; //找到第一个大于等于 nec 的点即可
int l=1,r=n-1,pos=n;
while(l<=r){
int mid=(l+r)>>1;
if(T.Query(1,mid)>=nec) pos=mid,r=mid-1;
else l=mid+1;
}
int ans=ord[pos];
for(int i=19;~i;--i) if(bz[ans][i]){
int tar=bz[ans][i];
if(T.Query(dfn[tar],dfn[tar]+siz[tar]-1)<=T.sum[1]/2) ans=bz[ans][i];
}
if(T.Query(dfn[ans],dfn[ans]+siz[ans]-1)<=T.sum[1]/2) ans=fa[ans];
print(ans);
}
return 0;
}
爆搜
观察到 个点的无向图最多有 组匹配,将 连一条虚边,那么所有的合法匹配一定将点集划分成了若干个环和链
使用虚边将 绑定成一个虚点,使用 的状压 来求每个集合表示成链/环的权值之和,剩下的工作是一个集合幂级数
链添加一维记录链尾,转移时因为 被绑定,添加一组虚点的时候可以据此得到新的链尾,注意每条链会被计算正反两次
计算环的方案数时从环上最大标号的点开始 ,加入的一对虚点标号不超过当前集合最大值即可,也要记录环尾,最后通过环尾和集合内最大值是否有边判定是否合法即可
Code Display
const int N=40;
int n,m,C,G[N][N],inv[N],ifac[N],fac[N];
int chain[1<<18],cyc[1<<18],dp[1<<18][40];
inline int in(int S,int id){
return S>>(id>>1)&1;
}
struct FPS{
int p[19]; FPS(){memset(p,0,sizeof(p));}
FPS operator +(const FPS &a)const{
FPS res;
rep(i,0,m) res.p[i]=add(p[i],a.p[i]);
return res;
}
FPS operator -(const FPS &a)const{
FPS res;
rep(i,0,m) res.p[i]=del(p[i],a.p[i]);
return res;
}
}f[1<<18];
inline void Exp(int *f){
vector<int> g(m+1);
g[0]=1;
for(int i=1;i<=m;++i){
for(int j=0;j<i;++j) ckadd(g[i],mul(f[j+1],mul(g[i-j-1],j+1)));
ckmul(g[i],inv[i]);
}
for(int i=0;i<=m;++i) f[i]=g[i];
return ;
}
signed main(){
freopen("dfs.in","r",stdin); freopen("dfs.out","w",stdout);
n=39; inv[0]=fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
Down(i,n,1) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
n=read(); m=read(); C=read(); if(n&1) ++n;
while(m--){
int u=read()-1,v=read()-1;
G[u][v]++; G[v][u]++;
}
m=(n+1)/2;
int S=1<<m;
for(int i=0;i<m;++i){
dp[1<<i][i<<1]=1;
if(2*i+1<=n) dp[1<<i][i<<1|1]=1;
}
for(int i=0;i<S;++i){
for(int j=0;j<n;++j) if(dp[i][j]){
for(int k=0;k<n;++k) if(!in(i,k)&&G[j][k]){
ckadd(dp[i^(1<<(k>>1))][k^1],mul(C,dp[i][j]));
}
}
}
for(int i=1;i<S;++i){
for(int j=0;j<n;++j) ckadd(chain[i],dp[i][j]);
ckmul(chain[i],inv[2]);
}
memset(dp,0,sizeof(dp));
for(int i=0;i<m;++i) dp[1<<i][i<<1]=1;
for(int i=1;i<S;++i){
int hig=63-__builtin_clzll(i);
for(int j=0;j<=hig*2+1;++j) if(dp[i][j]){
for(int k=0;k<=hig*2+1;++k) if(!in(i,k)&&G[j][k]){
ckadd(dp[i^(1<<(k>>1))][k^1],mul(C,dp[i][j]));
}
}
}
for(int i=1;i<S;++i){
int hig=63-__builtin_clzll(i);
for(int j=0;j<=2*hig+1;++j) ckadd(cyc[i],mul(G[j][hig<<1|1],dp[i][j]));
ckmul(cyc[i],C);
}
for(int i=0;i<S;++i) f[i].p[__builtin_popcount(i)]=add(cyc[i],chain[i]);
for(int p=2;p<=S;p<<=1){
int len=p>>1;
for(int k=0;k<S;k+=p) for(int l=k;l<k+len;++l) f[l+len]=f[l+len]+f[l];
}
for(int i=0;i<S;++i) Exp(f[i].p);
for(int p=2;p<=S;p<<=1){
int len=p>>1;
for(int k=0;k<S;k+=p) for(int l=k;l<k+len;++l) f[l+len]=f[l+len]-f[l];
}
print(f[S-1].p[m]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律