matrix tree
矩阵aij,表示i和j的lca的点权值,询问矩阵的行列式
考虑首先将列按dfs序,依次开始高斯消元,假设是1-2-3,第一行第一列必定全都是w[1],因此可以直接消元,消完之后考虑2,此时所有跟2的lca为1的点必定不再2的子树中,假设是i号点,则a2i=ai2=w[1]-w[1]=0,因此这些行是不需要用2这一行去减的,而跟2的lca不为1的点,lca必定等于2,即a2j=aj2=w[2]-w[1],因此也可以直接减,此时考虑3,若3是2的兄弟,其实情况和2相同,若是2的子节点,那么所有lca(3,i)=1就有a3i=ai3=0,因为被1消过,却不受2的影响,而lca(3,i)=2的情况,在2消之前,a3i=ai3=w[2]-w[1],而a2i=ai2=w[2]-w[1],故2消过之后,a3i=ai3=0,情况又跟2的情况类似,如此可以归纳一下,每次都只用把子树所在的行减一次。
最后的结果可以观察主对角线就是w[1]*pi(w[i]-w[fa[i]])
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define next nex
const int mo=1000000007;
using namespace std;
long long ans;
int n,top;
long long w[500000];
int tail[500000],ss,v[500000],st[500000];
int next[2000000],sora[2000000];
void origin()
{
ss=n;
for (int i=1;i<=n;i++) tail[i]=i,next[i]=0;
}
void link(int x,int y)
{
++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0;
++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0;
}
void bfs(int s)
{
int h,r,ne,na;
h=r=0;
st[r=1]=s,v[s]=1;
ans=w[s];
for (;h<r;) {
ne=st[++h];
for (int i=ne;next[i];) {
i=next[i],na=sora[i];
if (!v[na]) {
v[na]=1;
st[++r]=na;
ans=(ans*(w[na]-w[ne]))%mo;
}
}
}
}
void dfs(int x)
{
v[x]=1;
st[x]=++top;
for (int i=x,ne;next[i];) {
i=next[i],ne=sora[i];
if (!v[ne]) ans=(ans*(w[ne]-w[x])%mo),dfs(ne);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&w[i]);
origin();
for (int i=1;i<=n-1;i++) {
int x,y;
scanf("%d%d",&x,&y);
link(x,y);
}
// bfs(1);
top=0;
ans=w[1];
dfs(1);
for (int i=1;i<=n;i++) v[i]=0;
for (int i=1;i<=n;i++)
if (!v[i]) {
int x=i;
v[x]=1;
if ((abs(x-st[x])&1)!=0) ans*=-1;
for (x=st[x];x!=i;v[x]=1,x=st[x])
if ((abs(x-st[x])&1)!=0) ans*=-1;
}
cout<<(ans+mo)%mo<<endl;
return 0;
}
Esp
询问一列数,任选k个数的和的积。
观察一下其实就是求pi(1+ai*X)的每一项系数是多少。
直接分治成两半,用FFT合并即可。
一个实现细节就是FFT合并完之后要记得降次,否则会有越来越多前导零。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
const int mo=100003;
const long double pi=acos(-1.0);
using namespace std;
struct Complex {
long double x, y;
inline Complex (long double real = 0, long double imag = 0) : x(real), y(imag) {}
long double &real() {
return x;
}
long double &imag() {
return y;
}
void print()
{
cout<<"real="<<x<<" imag="<<y<<endl;
}
}Pa[2000000],Pb[2000000],Pc[2000000];
int ans[500000];
vector <int> tmp;
int st[20][2][500000];
int n,q,a[2000000];
inline Complex operator+(const Complex &a, const Complex &b) {
return Complex(a.x + b.x, a.y + b.y);
}
inline Complex operator-(const Complex &a, const Complex &b) {
return Complex(a.x - b.x, a.y - b.y);
}
inline Complex operator*(const Complex &a, const Complex &b) {
return Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
}
inline void sincos(long double theta,long double &a,long double &b)
{
a=sin(theta);
b=cos(theta);
}
void FFT(Complex P[], int n, int oper)
{
for (int i = 1, j = 0; i < n - 1; i++) {
for (int s = n; j ^= s >>= 1, ~j & s;);
if (i < j) {
swap(P[i], P[j]);
}
}
Complex unit_p0;
for (int d = 0; (1 << d) < n; d++) {
int m = 1 << d, m2 = m * 2;
long double p0 = pi / m * oper;
// sincos(p0, &unit_p0.y, &unit_p0.x);
sincos(p0,unit_p0.y,unit_p0.x);
for (int i = 0; i < n; i += m2) {
Complex unit = 1;
for (int j = 0; j < m; j++) {
Complex &P1 = P[i + j + m], &P2 = P[i + j];
Complex t = unit * P1;
P1 = P2 - t;
P2 = P2 + t;
unit = unit * unit_p0;
}
}
}
}
void calc(int l,int r,int e,int ans[])
{
if (l==r) {
ans[0]=2,ans[1]=1,ans[2]=a[l]%mo;
return ;
}
int mid=(l+r)>>1;
calc(l,mid,e+1,st[e][0]);
calc(mid+1,r,e+1,st[e][1]);
int la=st[e][0][0],lb=st[e][1][0],N;
for (N=1;N<=la+lb+1;N<<=1) ;
for (int i=0;i<la;i++) Pa[i]=Complex(st[e][0][i+1],0);
for (int i=la;i<N;i++) Pa[i]=Complex(0,0);
for (int i=0;i<lb;i++) Pb[i]=Complex(st[e][1][i+1],0);
for (int i=lb;i<N;i++) Pb[i]=Complex(0,0);
FFT(Pa,N,1),FFT(Pb,N,1);
for (int i=0;i<N;i++) Pc[i]=Pa[i]*Pb[i];
FFT(Pc,N,-1);
ans[0]=N;
for (int i=0;i<N;i++) {
int x=((long long)((Pc[i].x/N)+0.5))%mo;
ans[i+1]=x;
}
for (;ans[0]>1 && !ans[ans[0]];ans[0]--) ;
// cout<<l<<' '<<r<<' '<<N<<endl;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
calc(1,n,0,ans);
scanf("%d",&q);
for (int i=1;i<=q;i++) {
int k;
scanf("%d",&k);
printf("%d\n",ans[k+1]);
}
return 0;
}
swap permutation
询问一个排列,相邻交换严格k次,任意交换最多k次得到的排列个数是多少
考虑将第i个数放入前i-1个数的方案
用f[i][j]表示前i个数相邻交换严格j次的答案是多少,由于i在i-1个数中能插的位置是i个,然后又最多只能用j次交换往前,因此f[i][j]=f[i-1][j]+f[i-1][j-1]+...+f[i-1][j-i+1]
然后考虑到i和i+1换两次就换回来不变了,因此最后统答案的时候,将与k同奇偶的j的f[n][j]累和即可
第二问用g[i][j]表示前i个数用了严格j次交换的方案是多少
同样考虑把i插进去,首先是不动就是g[i-1][j],然后是一次可以与前i-1个数任意交换,因此就是g[i-1][j-1]*(i-1)
则g[i][j]=g[i-1][j]+g[i-1][j-1]*(i-1)
最后统答案的时候就把比k小的j的g[n][j]累和即可
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int mo=1000000007;
using namespace std;
int n,k;
int f[5000][5000],s[5000][5000];
long long g[5000][5000];
int main()
{
scanf("%d%d",&n,&k);
f[0][0]=1;
for (int j=0;j<=k;j++) s[0][j]=f[0][0];
for (int i=1;i<=n;i++)
for (int j=0;j<=k;j++) {
int L=j-i,R=j;
if (L>=0)
f[i][j]=(s[i-1][R]-s[i-1][L]+mo)%mo;
else f[i][j]=s[i-1][R];
if (j!=0) s[i][j]=(s[i][j-1]+f[i][j])%mo;
else s[i][j]=f[i][j];
}
int ans=0;
for (int j=0;j<=k;j++)
if ((j&1)==(k&1)) ans=(ans+f[n][j])%mo;
printf("%d ",ans);
g[0][0]=1;
for (int i=1;i<=n;i++)
for (int j=0;j<=k;j++) {
g[i][j]=g[i-1][j];
if (j) g[i][j]=(g[i][j]+g[i-1][j-1]*(i-1))%mo;
}
ans=0;
for (int j=0;j<=k;j++)
ans=(ans+g[n][j])%mo;
printf("%d\n",ans);
return 0;
}