本文共 1955 字,大约阅读时间需要 6 分钟。
传送门:
题目要求计算Ai^Aj < Aj^Ak的种数
题解:字典树
对于这道题首先考虑到,假如Ai和Ak前t-1位都相同,第t位不相同时,如果Ai的第t位等于Aj的第t位,Ak的第t位不等于Aj的第t位,那么Ai^Aj一定小于Aj^Ak的,那么只要在字典树中从高位往低位依次考虑即可。
v表示这个节点添加的次数,sum表示在这个数的前面有多少个数的前t-1位与这个数的前t-1位是不同的(第t位相同),cnt[i][0/1]表示有多少个数字的第i位是0/1
枚举Ak的每一位,对于Ak的第t位记作tmp
现在只讨论如果tmp==1时对答案的贡献(tmp==0时的求法相同)
L[now].nxt[tmp^1].v*(L[now].nxt[tmp^1].v-1)/2+(cnt[t][tmp^1]-L[now].nxt[tmp^1].v)*L[now].nxt[tmp^1].v-L[now].sum[tmp^1]
1)前一部分表示Ai,Aj和Ak的前t-1位都相同时对答案的贡献
2)中间一部分是Ai的前t-1位与Ak的前t-1相同,Aj的前t-1位不与Ak的前t-1相同时对答案的贡献(乍看一下第二部分没有问题,其实里面包含了非法部分,即有些前t-1位与Ak不相同的数被记作了Ai,因此需要第三部分)
3)后面一部分表示Ai的前t-1位与Ak的前t-1位不同时对答案的贡献(为负数,因为计算时规定了Ai的前t-1位要与Ak的前t-1位相同)
#include#include #include #include #include #include #include #include #define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define first x#define second y#define eps 1e-5using namespace std;typedef long long LL;typedef pair PII;const int inf = 0x3f3f3f3f;const int MX = 5e7 + 5;struct node { int nxt[2]; LL sum[2]; int v; void init() { sum[0] = sum[1] = 0; nxt[0] = nxt[1] = -1; v = 0; }} L[MX];int tot;LL ans, cnt[32][2];void add(int a[], int len) { int now = 0, tmp; for (int i = len - 1; i >= 0; i--) { tmp = a[i]; if (L[now].nxt[tmp] == -1) { L[++tot].init(); L[now].nxt[tmp] = tot; } if (L[now].nxt[tmp ^ 1] != -1) { ans += L[L[now].nxt[tmp ^ 1]].v * (L[L[now].nxt[tmp ^ 1]].v - 1) / 2; ans += L[L[now].nxt[tmp ^ 1]].v * (cnt[i][tmp ^ 1] - L[L[now].nxt[tmp ^ 1]].v); ans -= L[now].sum[tmp ^ 1]; } L[now].sum[tmp] += cnt[i][tmp] - L[L[now].nxt[tmp]].v; cnt[i][tmp] ++; now = L[now].nxt[tmp]; L[now].v++; }}int a[500005], b[35];int main() { //freopen("in.txt", "r", stdin); int T, n; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); memset(cnt, 0, sizeof(cnt)); tot = ans = 0; L[0].init(); for (int i = 1; i <= n; i++) { for (int j = 0; j < 30; j++) b[j] = a[i] >> j & 1; add(b, 30); } printf("%lld\n", ans); } return 0;}
转载地址:http://ipwpn.baihongyu.com/