因为题目相比其他几次水一点所以就写一起了。
问有 1 到 n n n 这 n n n 个元素的二叉堆个数,模 1 0 9 + 7 10^9+7 109+7。 n ≤ 1 0 9 n\leq 10^9 n≤109。
记 s z i sz_i szi 为编号为 i i i 的结点的子树的大小,知道 s z i sz_i szi 后我们很容易能够算出一个节点左右子树的大小。
以下有两种推导方式:
分子部分的阶乘显然不能现场算,于是分段打表。分母部分可以考虑 g s z x = g s z l s o n ⋅ g s z r s o n ⋅ s z x g_{sz_x}=g_{sz_{lson}}\cdot g_{sz_{rson}}\cdot sz_x gszx=gszlson⋅gszrson⋅szx,记忆化一下就是 O ( log n ) O(\log n) O(logn)。
总复杂度为 O ( log n + n k ) O(\log n+{n\over k}) O(logn+kn)。其中 k k k 是分段打表的段数。
代码:
#include
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=1e6+10,mod=1e9+7;
map<int,int>f;
map<int,int>ff;
int fac[N],ifac[N];
void init_fac(int n){
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*1ll*i%mod;
ifac[0]=ifac[1]=1;
for(int i=2;i<=n;i++)ifac[i]=ifac[mod%i]*1ll*(mod-mod/i)%mod;
for(int i=1;i<=n;i++)ifac[i]=ifac[i-1]*1ll*ifac[i]%mod;
}
int getfac(int x){ return fac[x]; }
int getifac(int x){ return ifac[x]; }
int cnt=0;
int solve(int n){
if(n<=2)return 1;
map<int,int>::iterator it=f.find(n);
if(it!=f.end())return it->second;
++cnt;
int t=1;
while(t<<1<=n+1)t<<=1;--t;
int l=min(t,n-(t>>1)-1),r=n-l-1;
int c=getfac(l+r)*1ll*getifac(l)%mod*getifac(r)%mod;
return f[n]=c*1ll*solve(l)%mod*solve(r)%mod;
}
int table[]={1,491101308,723816384,27368307,199888908,927880474,661224977,970055531,195888993,547665832,933245637,
368925948,136026497,135498044,419363534,668123525,30977140,309058615,189239124,940567523,429277690,
358655417,780072518,275105629,99199382,733333339,608823837,141827977,637939935,848924691,724464507,
326159309,903466878,769795511,606241871,957939114,852304035,375297772,624148346,624500515,203191898,
629786193,814362881,116667533,627655552,586445753,193781724,83868974,965785236,377329025,698611116},step=2e7;
int solvee(int n){
if(n<=1)return 1;
map<int,int>::iterator it=ff.find(n);
if(it!=ff.end())return it->second;
//cerr<<"> "<
int t=1;
while(t<<1<=n+1)t<<=1;--t;
int l=min(t,n-(t>>1)-1),r=n-l-1;
return ff[n]=n*1ll*solvee(l)%mod*solvee(r)%mod;
}
int qpow(int x,int y){
int ans=1;
while(y){
if(y&1)ans=ans*1ll*x%mod;
x=x*1ll*x%mod;
y>>=1;
}
return ans;
}
int main(){
int n=getint();
if(n<=1e6){
init_fac(n);
cout<<solve(n)<<endl;
//cerr<
return 0;
}
int t=n/step;
int ans=table[t];
//cerr<
for(int i=step*t+1;i<=n;i++)ans=ans*1ll*i%mod;
ans=ans*1ll*qpow(solvee(n),mod-2)%mod;
cout<<ans<<endl;
return 0;
}
有一个长为 n n n 的未知序列 a a a,每个位置已知权值 w i w_i wi。你可以花费 ⨁ i = l r w i \bigoplus\limits_{i=l}^r w_i i=l⨁rwi 的代价获得 ⨁ i = l r a i \bigoplus\limits_{i=l}^r a_i i=l⨁rai 的值。问:知道花费多少代价才能知道所有 a i a_i ai 的值。 n ≤ 1 0 5 n\leq 10^5 n≤105, w i ≤ 1 0 9 w_i\leq 10^9 wi≤109。
记 s i s_i si 为 a i a_i ai 的前缀异或和,记 t i t_i ti 为 w i w_i wi 的前缀异或和。特别的, s 0 = t 0 = 0 s_0=t_0=0 s0=t0=0。
显然得到所有 a i a_i ai 就是得到所有 s i s_i si,而花费 ⨁ i = l r w i \bigoplus\limits_{i=l}^r w_i i=l⨁rwi 的代价获得 ⨁ i = l r a i \bigoplus\limits_{i=l}^r a_i i=l⨁rai 的值就相当于花费 t r ⊕ t l − 1 t_r\oplus t_{l-1} tr⊕tl−1 的代价获得 s r ⊕ s l − 1 s_r\oplus s_{l-1} sr⊕sl−1 的值。
我们一开始只知道 s 0 = 0 s_0=0 s0=0。
一次查询实际上可以看作在两点之间用给定代价连边;当所有点都与 s 0 s_0 s0 直接或间接连通时,我们就可以推出所有 s i s_i si 的值,也就是要求这个图的最小生成树。
于是这题就等价于 CF888G Xor-MST 了。
代码:
#include
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=1e5+10,L=30,M=N*L;
int ch[M][2],sz[M],cnt=1;
void add(int x){
int u=1;
for(int i=L-1;i>=0;--i){
int t=(x>>i)&1;
sz[u]++;
if(!ch[u][t])ch[u][t]=++cnt;
u=ch[u][t];
}
sz[u]++;
}
int query(int x,int val,int l){
int ans=0;
for(int i=l;i>=0;--i){
int t=(val>>i)&1;
if(ch[x][t])x=ch[x][t];
else x=ch[x][t^1],ans|=(1<<i);
}
return ans;
}
vector<int>v;
long long ans=0;
void solve(int u,int val,int l){
//cerr<<"solve "<
if(l<0){
for(int i=0;i<sz[u];i++)v.push_back(val);
//cerr<<">> "<
return;
}
if(!ch[u][0]){
solve(ch[u][1],val|(1<<l),l-1);
return;
}
if(!ch[u][1]){
solve(ch[u][0],val,l-1);
return;
}
int c1=ch[u][0],c2=ch[u][1];
if(sz[c1]>sz[c2])swap(c1,c2);
solve(c2,val|((c2==ch[u][1])<<l),l-1);
int qaq=v.size();
solve(c1,val|((c1==ch[u][1])<<l),l-1);
int mn=0x7f7f7f7f;
for(int i=qaq;i<v.size();i++){
mn=min(mn,(1<<l)|query(c2,v[i],l-1));
}
ans+=mn;
return;
}
int a[N];
int main(){
int n=getint();
add(0);
for(int i=1;i<=n;i++){
a[i]=getint()^a[i-1];
add(a[i]);
//cerr<<"add "<
}
solve(1,0,L-1);
cout<<ans;
return 0;
}
有两棵树 A A A 与 B B B,大小分别为 n n n 与 m m m,问有多少 A A A 的连通子图与 B B B 同构,答案模 1 0 9 + 7 10^9+7 109+7。 n ≤ 2000 n\leq 2000 n≤2000, m ≤ 12 m\leq 12 m≤12。
一开始以为 A A A 是一张图然后就没法做了。
首先判断两棵树同构的方式是:选定一个根,对于每个节点排序其所有子树的树哈希,然后再把这些树哈希按正常方式合并起来。
于是枚举 B B B 中的每个点(以之为根时形态相同者不再重复枚举),对于每个有相同形态子树的节点,给它记录一个系数 g [ i ] g[i] g[i] 为各种形态子树的数量的阶乘之积的倒数(相同形态的子树任意交换后都是重复的)。然后在 A A A 上 DP,记录 d p [ i ] [ j ] dp[i][j] dp[i][j] 为 A A A 中以 i i i 为根,与 B B B 中 j j j 的子树同构的子图的个数,转移时枚举子树,状压一下。
代码:
#include
using namespace std;
#define ll long long
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const ll mod=1e9+7,G=19;
const int N=2010,M=14;
struct bian{
int e,n;
};
struct graph{
bian b[N<<1];
int s[N],tot=0;
void add(int x,int y){
++tot;
b[tot].e=y;
b[tot].n=s[x];
s[x]=tot;
}
} A,B;
int m,n;
ll qpow(int x,int y){
int ans=1;
while(y){
if(y&1)ans=ans*1ll*x%mod;
x=x*1ll*x%mod;
y>>=1;
}
return ans;
}
ll pw[N];
int ifac[N];
ll h[N];
int sz[N],g[N];
vector<int>ch[N];
bool cmp(int x,int y){ return h[x]<h[y]; }
void ss1(int x,int f){
//cerr<<"ss1 "<
ch[x].clear();h[x]=0;sz[x]=1;
for(int i=B.s[x];i;i=B.b[i].n){
if(B.b[i].e==f)continue;
ss1(B.b[i].e,x);
ch[x].push_back(B.b[i].e);
}
sort(ch[x].begin(),ch[x].end(),cmp);
for(int i=0;i<ch[x].size();i++){
int v=ch[x][i];
//cerr< "<
h[x]=h[x]*pw[sz[v]<<1]+h[v];
sz[x]+=sz[v];
}
h[x]=h[x]*pw[sz[x]*2-1]+1;
//cerr<<"h "<
g[x]=1;int l=0;
for(int i=0;i<=ch[x].size();i++){
if(i==ch[x].size()||h[ch[x][i]]!=h[ch[x][l]]){
g[x]=g[x]*1ll*ifac[i-l]%mod;
l=i;
}
}
}
int dp[N][M];
void ss(int x,int fa){
for(int i=A.s[x];i;i=A.b[i].n){
int v=A.b[i].e;
if(v==fa)continue;
ss(v,x);
}
for(int i=1;i<=m;i++){
static int ic[M];
int ct=0;
for(int j=1;j<=m;j++)ic[j]=-1;
for(int j=0;j<ch[i].size();j++)ic[ch[i][j]]=ct++;
if(!ct){
//cerr<<"!ct "<
dp[x][i]=1;continue;
}
static int f[2][1<<M];
int S=1<<ct,t=0;
for(int j=0;j<S;j++)f[0][j]=0;f[0][0]=1;
for(int j=A.s[x];j;j=A.b[j].n){
int v=A.b[j].e;
if(v==fa)continue;
for(int k=0;k<S;k++)f[t^1][k]=0;
for(int k=1;k<=m;k++){
if(~ic[k]){
for(int l=(1<<ic[k]);l<S;l=(l+1)|(1<<ic[k])){
f[t^1][l]+=f[t][l^(1<<ic[k])]*1ll*dp[v][k]%mod;
if(f[t^1][l]>=mod)f[t^1][l]-=mod;
}
}
}//for k
t^=1;
for(int k=0;k<S;k++){
f[t][k]+=f[t^1][k];
if(f[t][k]>=mod)f[t][k]-=mod;
}
}//for j
dp[x][i]=g[i]*1ll*f[t][S-1]%mod;
//cerr<<"dp "<
}//for i
}
int main(){
n=getint();
for(int i=1;i<n;i++){
int x=getint(),y=getint();
A.add(x,y); A.add(y,x);
}
m=getint();
for(int i=1;i<m;i++){
int x=getint(),y=getint();
B.add(x,y); B.add(y,x);
}
pw[0]=1;for(int i=1;i<=m*2;i++)pw[i]=pw[i-1]*1ll*G;
ifac[0]=ifac[1]=1;for(int i=2;i<=m;i++)ifac[i]=(ifac[mod%i]*1ll*(mod-mod/i))%mod;
for(int i=1;i<=m;i++)ifac[i]=ifac[i-1]*1ll*ifac[i]%mod;
ll ans=0;set<int>se;
for(int i=1;i<=m;i++){
ss1(i,0);
//cerr<<">"<
if(!se.insert(h[i]).second)continue;
ss(1,0);
for(int j=1;j<=n;j++){
ans+=dp[j][i];
if(ans>=mod)ans-=mod;
}
}
cout<<ans;
}