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(64∗64∗logn∗(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
*/