Tree,2017 ACM-ICPC 亚洲区(西安赛区)网络赛A,点分治

2017 ACM-ICPC 亚洲区(西安赛区)网络赛 A Tree

https://nanti.jisuanke.com/t/A1267
There is a tree with nn nodes, at which attach a binary 64*6464∗64 matrix M_i (1 \le i \le n)M
i

(1≤i≤n). There are qq queries for matrix multiplication on the path from node aa to node bb modulo 22. To avoid massive input dataset, M_i(1\le i \le n)M
i

(1≤i≤n) is attained by the following algorithm:

Input a random seedseed (unsigned long long)

思路:首先对于所有询问(u.v),将每个点u需要计算路径的所有v记录下来,因为矩阵乘法不满足交换律,记得标记下是u到v还是v到u,然后点分治时对于一个重心root,可以分别处理它的每一颗子树,对于子树上的每一个节点,算出它到子树根节点的矩阵乘积(由子树根到该节点以及反方向的都需要算出来),并且看该节点u是否有一个对应节点v需要询问答案,并且节点v出现在其他子树中,出现的话将答案计算出来即可
因为点分治有logn层,每层访问所有节点需要询问的答案,也就n+q的复杂度,然后因为是64位的01矩阵,压位后矩阵乘复杂度为64*64,最终复杂度为 O ( 64 ∗ 64 ∗ l o g n ∗ ( n + q ) O(64*64*logn*(n+q) O(6464logn(n+q)
(初始化有些细节没注意调一万年。。。)

#include<bits/stdc++.h>
#define MAXN 3010
#define MAXM 30010
#define MOD 19260817
#define INF 0x3f3f3f3f
#define ull unsigned long long
#define ll long long
using namespace std;
int head[MAXN],tot;
struct edge
{
    int v,nxt;
}edg[MAXN << 1];
inline void addedg(int u,int v)
{
    edg[tot].v = v;
    edg[tot].nxt = head[u];
    head[u] = tot++;
}
struct matrix
{
    ull a[64];
}mat[MAXN],pre[MAXN],sub[MAXN],mm;
inline matrix mul(matrix n1,matrix n2)
{
    matrix ans;
    for(int i = 0;i < 64;++i)
    {
        ans.a[i] = 0;
        for(int j = 0;j < 64;++j)
            if(n1.a[i] & (1ull <<(63-j)))
                ans.a[i] ^= n2.a[j];
    }
    return ans;
}
int mx,Size,root,sz[MAXN];
bool vis[MAXN];
inline void getroot(int u,int f)
{
    int mson = 0,v;
    sz[u] = 1;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v] || v == f) continue;
        getroot(v,u);
        sz[u] += sz[v];
        mson = max(mson,sz[v]);
    }
    mson = max(mson,Size-sz[u]);
    if(mson < mx) mx = mson,root = u;
}
ll pw19[65],pw26[65];
inline ll cal(matrix ma)
{
    ll ans = 0;
    for(int i = 0;i < 64;++i)
        for(int j = 0;j < 64;++j)
            if(ma.a[i] & (1ull << (63-j)))
                ans = (ans + pw19[i+1]* pw26[j+1] % MOD)%MOD;
    return ans;
}
int n,q;
ll ans[MAXM];
struct node
{
    int v,tp,id;
};
vector<node> ve[MAXN];
ull seed;
bool vv[MAXN];
int vvid[MAXN],cc,cnt,id[MAXN];
matrix dispre[MAXN],dissub[MAXN];
inline void getdis(int u,int f,matrix pr,matrix sb)
{
    int v;
    pr = mul(pr,mat[u]);
    sb = mul(mat[u],sb);
    dispre[u] = pr,dissub[u] = sb,id[++cnt] = u;
    for(int i = 0;i < ve[u].size();++i)
    {
        if(vv[ve[u][i].v])
        {
            if(ve[u][i].tp == 1)
                ans[ve[u][i].id] = cal(mul(sb,pre[ve[u][i].v]));
            else
                ans[ve[u][i].id] = cal(mul(sub[ve[u][i].v],pr));
        }
    }
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(v == f || vis[v]) continue;
        getdis(v,u,pr,sb);
    }
}
inline void solve(int u,int ssize)
{
    vis[u] = 1;
    cc = 0;
    vv[u] = 1;
    vvid[++cc] = u,pre[u] = sub[u] = mat[u];
    for(int i = 0;i < ve[u].size();++i)
    {
        if(ve[u][i].v == u)
            ans[ve[u][i].id] = cal(mat[u]);
    }
    int v;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v]) continue;
        cnt = 0;
        getdis(v,v,mm,mm);
        for(int j = 1;j <= cnt;++j)
        {
            pre[id[j]] = mul(mat[u],dispre[id[j]]),sub[id[j]] = mul(dissub[id[j]],mat[u]);
            vv[id[j]] = 1,vvid[++cc] = id[j];
        }
    }
    for(int i = 1;i <= cc;++i)
        vv[vvid[i]] = 0;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v]) continue;
        Size = sz[v] < sz[u] ? sz[v] : ssize - sz[u];
        mx = INF;
        getroot(v,v);
        solve(root,Size);
    }
}
inline void init()
{
    memset(head,-1,sizeof(int)*(n+1));
    memset(vis,false,sizeof(bool)*(n+1));
    for(int i = 1;i <= n;++i)
        ve[i].clear();
    tot = 0;
    Size = n,mx = INF;
}
int main()
{
    pw19[0] = pw26[0] = 1;
    for(int i = 1;i <= 64;++i)
    {
        pw19[i] = pw19[i-1]  * 19 % MOD;
        pw26[i] = pw26[i-1] * 26 % MOD;
    }
    for(int i = 0;i < 64;++i)
        mm.a[i] = (1ull << (63-i));
    while(~scanf("%d%d",&n,&q))
    {
        init();
        int u,v;
        for(int i = 1;i < n;++i)
        {
            scanf("%d%d",&u,&v);
            addedg(u,v);
            addedg(v,u);
        }
        scanf("%llu",&seed);
        for(int i = 1; i <= n; ++i) {
            memset(mat[i].a,0,sizeof(mat[i].a));
            for(int p = 0; p < 64; ++p) {
                seed ^= seed * seed + 15;
                for(int q = 0; q < 64; ++q) {
                    mat[i].a[p] |= ((((seed >> q) & 1)*1ull) << (63-q));
                }
            }
        }
        for(int i = 1;i <= q;++i)
        {
            scanf("%d%d",&u,&v);
            ve[u].push_back(node{v,1,i});
            if(u != v)
                ve[v].push_back(node{u,2,i});
        }
        getroot(1,1);
        solve(root,Size);
        for(int i = 1;i <= q;++i)
            printf("%lld\n",ans[i]);
    }
    return 0;
}
/*
 6 6
4 1
3 4
6 4
5 3
2 3
19260817
4 4
1 1
2 2
3 3
5 5
6 6
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值