传送门:点击打开链接
题意:告诉你如何构造一颗树,然后询问有多少点对的距离小于等于K(K<=10)
思路:用csy的话来说,这就是个傻逼题,然而比赛的时候就是傻逼不会- -
设dp[u][k]表示当节点u作为子树的根节点时,在这个子树中有多少点对与u的距离<=k
那么ans[u]=dp[u][k]+sigma(dp[v][k-i]-dp[vlast][k-i-1]) 1<=i<=k,i表示向父节点走的距离,v表示距离u为i的父节点,vlast表示距离u为k-1的父节点。
其实就是说,如果选u作为一个点对的其中一个点后,另一个点要么出现在它作为根节点所在的子树中,那么这部分答案就是dp[u][k]
另外有可能就是在除了这个子树的其他位置上。那么我就去向上找父节点,以父节点作为拐弯点,求出dp[v][k-i]的个数,因为又包括了自己子树的一部分,再减去dp[vlast][k-i-1]就搞定了。
只有一个要注意的地方,就是生成树的那个函数中,A*i可能会爆int如果不用LL可能会RE,还有就是前面那个公式i=k的时候,要特判一下,因为此时k-i-1会是负数
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; const int MX = 5e5 + 5; struct Edge { int v, nxt; } E[MX]; int Head[MX], rear = 0; void edge_init() { rear = 0; memset(Head, -1, sizeof(Head)); } void edge_add(int u, int v) { E[rear].v = v; E[rear].nxt = Head[u]; Head[u] = rear++; } int N, K, A, B; int f[MX], dp[MX][12]; void DFS(int u) { for(int i = 0; i <= K; i++) { dp[u][i] = 1; } for(int j = Head[u]; ~j; j = E[j].nxt) { int v = E[j].v; DFS(v); for(int i = 1; i <= K; i++) { dp[u][i] += dp[v][i - 1]; } } } void init() { f[1] = 0; edge_init(); memset(dp, 0, sizeof(dp)); for(int i = 2; i <= N; i++) { int pre = ((LL)A * i + B) % (i - 1) + 1; edge_add(pre, i); f[i] = pre; } DFS(1); } int solve() { int ret = 0; for(int u = 1; u <= N; u++) { int last = u, now, ans = dp[u][K]; for(int i = 1; i <= K; i++) { now = f[last]; if(!now) break; if(K == i) ans += 1; else ans += dp[now][K - i] - dp[last][K - i - 1]; last = now; } ret ^= ans; } return ret; } int main() { int T; //FIN; scanf("%d", &T); while(T--) { scanf("%d%d%d%d", &N, &K, &A, &B); init(); printf("%d\n", solve()); } return 0; }