LOJ6271 [长乐集训 2017 Day10] 生成树求和 加强版

先对三进制下的每一位进行考虑,类似 CF917D Stranger Trees 一样,对每条边赋权为 (1,x,x^2),矩阵树定理求行列式后用高斯消元或者插值求出多项式即可,但这样复杂度为 (O(n^4 log n))。因为循环卷积意义下的多项式的有用次数比较小,考虑直接代入多项式来求行列式,这里多项式的运算为模 (x^3) 意义下的循环卷积,代入 (3) 次单位根即可, (3) 次单位根有个性质:(omega_3^2=omega_3-1),用二元组 ((a,b)) 来表示一个数 (a+bomega_3),得其逆元为:

[large frac{1}{a+bomega_3}=frac{a-b-bomega_3}{(a+bomega_3)(a-b-bomega_3)}=frac{a-b}{a^2+b^2-ab}-frac{b}{a^2+b^2-ab}omega_3 ]

实际上因为 (3) 在本题的模数中存在二次剩余,因此二元组 ((a,b)) 也可以直接表示复数 (a+bi)。总复杂度为 (O(n^3log n))

#include<bits/stdc++.h>
#define maxn 210
#define maxm 40010
#define p 1000000007
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,mx;
ll ans;
struct edge
{
    int x,y,v;
    edge(int a=0,int b=0,int c=0)
    {
        x=a,y=b,v=c;
    }
}e[maxm];
ll inv(ll x)
{
    ll v=1,y=p-2;
    while(y)
    {
        if(y&1) v=v*x%p;
        x=x*x%p,y>>=1;
    }
    return v;
}
struct node
{
    ll a,b;
    node(ll x=0,ll y=0)
    {
        a=x,b=y;
    }
    node get()
    {
        ll v=inv(a*a%p+b*b%p-a*b%p+p);
        return node((a-b+p)*v%p,(p-b)*v%p);
    }
    bool empty()
    {
        return !a&&!b;
    }
}a[maxn][maxn],w[5],f[5],g[5];
node operator + (const node &x,const node &y)
{
    return node((x.a+y.a)%p,(x.b+y.b)%p);
}
node operator - (const node &x,const node &y)
{
    return node((x.a-y.a+p)%p,(x.b-y.b+p)%p);
}
node operator * (const node &x,const node &y)
{
    return node((x.a*y.a%p-x.b*y.b%p+p)%p,(x.a*y.b%p+x.b*y.a%p-x.b*y.b%p+p)%p);
}
node det()
{
    node v=node(1,0);
    for(int i=2;i<=n;++i)
    {
        if(a[i][i].empty())
        {
            for(int j=i+1;j<=n;++j)
            {
                if(!a[j][i].empty())
                {
                    swap(a[i],a[j]),v=node(0,0)-v;
                    break;
                }
            }
        }
        if(a[i][i].empty()) return node(0,0);
        node iv=a[i][i].get();
        for(int j=i+1;j<=n;++j)
        {
            node d=a[j][i]*iv;
            for(int k=i;k<=n;++k) a[j][k]=a[j][k]-a[i][k]*d;
        }
        v=v*a[i][i];
    }
    return v;
}
int main()
{
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    read(n),read(m),w[0]=w[3]=node(1,0),w[1]=node(0,1),w[2]=node(p-1,p-1);
    for(int i=1;i<=m;++i)
        read(e[i].x),read(e[i].y),read(e[i].v),mx=max(mx,e[i].v);
    for(int k=1;k<=mx;k*=3)
    {
        for(int i=0;i<3;++i)
        {
            memset(a,0,sizeof(a));
            for(int j=1;j<=m;++j)
            {
                int x=e[j].x,y=e[j].y;
                node v=w[e[j].v/k%3*i%3];
                a[x][x]=a[x][x]+v,a[y][y]=a[y][y]+v;
                a[x][y]=a[x][y]-v,a[y][x]=a[y][x]-v;
            }
            f[i]=det(),g[i]=node(0,0);
        }
        for(int i=0;i<3;++i)
            for(int j=2;j>=0;--j)
                g[i]=g[i]*w[3-i]+f[j];
        ans=(ans+(g[1].a+g[2].a*2)*inv(3)%p*k%p)%p;        
    }
    printf("%lld",ans);   
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/14451414.html