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+b\omega_3\),得其逆元爲:node

\[\large \frac{1}{a+b\omega_3}=\frac{a-b-b\omega_3}{(a+b\omega_3)(a-b-b\omega_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^3\log n)\)c++

#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;
}
相關文章
相關標籤/搜索