「BZOJ3689」「FJ2014集训」异或之
「题目描述」
给定n个非负整数A[1], A[2], ……, A[n]。
对于每对(i, j)满足1 <= i < j <= n,得到一个新的数A[i] xor A[j],这样共有n*(n-1)/2个新的数。求这些数(不包含A[i])中前k小的数。
注:xor对应于pascal中的“xor”,C++中的“^”。
「输入格式」
第一行2个正整数 n,k,如题所述。
以下n行,每行一个非负整数表示A[i]。
「输出格式」
共一行k个数,表示前k小的数。
「样例输入」
4 5
1
1
3
4
「样例输出」
0 2 2 5 5
「样例解释」
1 xor 1 = 0 (A[1] xor A[2])
1 xor 3 = 2 (A[1] xor A[3])
1 xor 4 = 5 (A[1] xor A[4])
1 xor 3 = 2 (A[2] xor A[3])
1 xor 4 = 5 (A[2] xor A[4])
3 xor 4 = 7 (A[3] xor A[4])
前5小的数:0 2 2 5 5
「数据范围」
第一个数据点,n <= 1000;
第二个数据点,k = 1;
对于40%的数据,n <= 10000; k <= 10;
对于60%的数据,n <= 20000;
对于100%的数据,2 <= n <= 100000; 1 <= k <= min{250000, n*(n-1)/2};
0 <= A[i] < 231
「题解」
做法很多。。。有什么可持久字典树之类的真是高贵啊
我的做法比较坑。。。
从最高位开始,将该位0和1的分成俩组
不同组的异或值肯定比同组的大
这样每层扫一遍分俩半,递归下去30层,那么可以算出每层每块内自我匹配所能产生的数
从上往下扫每一层,如果某一层小于k那么就将前一层产生的数全部拿出来排序
因为数据是随机的所以这样就可以了。。。
但是zgg告诉我们,复杂度其实是有保证的
如果某层产生数小于k个,那么前一层不会超过2k个,所以排序复杂度是klogk
总复杂度30n+klogk
如果有手动构造的数据应该要特判最后一层如果某一块的平方大于k,直接输出k个0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define ll long long #define inf 100000000 using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,num; int a[100005],b[1000005]; int bin[35],tmp[35][100005]; ll cnt[35]; void solve1() { for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) b[++num]=(a[i]^a[j]); sort(b+1,b+num+1); for(int i=1;i<=m;i++) printf("%d ",b[i]); } void cal(int l,int r,int k) { if(l>=r)return; int tot=0; for(int i=l;i<=r;i++) if(tmp[k+1][i]&bin[k])tot++; int L=l-1,R=l+tot-1; for(int i=l;i<=r;i++) if(tmp[k+1][i]&bin[k])tmp[k][++L]=tmp[k+1][i]; else tmp[k][++R]=tmp[k+1][i]; if(k!=0){cal(l,l+tot-1,k-1);cal(l+tot,r,k-1);} cnt[k]+=(ll)(r-l+1)*(r-l)/2; } void print(int l,int r,int k,int x) { if(l>=r)return; if(k==x) { for(int i=l;i<=r;i++) for(int j=i+1;j<=r;j++) b[++num]=tmp[x+1][i]^tmp[x+1][j]; return; } int tot=0; for(int i=l;i<=r;i++) if(tmp[k+1][i]&bin[k])tot++; if(k!=0){print(l,l+tot-1,k-1,x);print(l+tot,r,k-1,x);} } void solve2() { for(int i=1;i<=n;i++)tmp[31][i]=a[i]; cal(1,n,30); bool flag=0; for(int i=30;i>=0;i--) if(cnt[i]<m){print(1,n,30,i+1);flag=1;break;} if(!flag)print(1,n,30,0); sort(b+1,b+num+1); for(int i=1;i<=m;i++) printf("%d ",b[i]); } int main() { //freopen("xorit.in","r",stdin); //freopen("xorit.out","w",stdout); bin[0]=1;for(int i=1;i<=31;i++)bin[i]=bin[i-1]*2; n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(); if(n<=1000)solve1(); else solve2(); return 0; } |