P5283 十二省联考 2019 异或粽子
原题
本题相当于计算查找k个(i,j)的异或值最大的范围的总和.
与计算最长异或路径p4551的思路很像,建议先写那道题.首先,我们再计算一条最长路径的时候,由于我们是将每一条路径都单独拿出来进行异或计算,所以,假设我们计算i,j的路径是最长的,我们会最终计算两遍,从1->i开始遍历时选择1->j这条路,而1->j开始遍历时同样会计算1->i这条路,所以一条最长路径会被计算两边(将p4551写出来即可理解为什么1->i,1->j都要分别遍历).所以,我们找k条实际需要我们找2k条,将2k个最大路径总和算出来后相加/2便是我们要的答案.
那么,怎么计算第k大的路径呢,这里,我们可以多定义一个数组,例如siz[cnt],表示经过第cnt个节点的次数,pat[i][k],用来表示以1->i为起点,异或和第k大的路径,当我们进行异或操作时,优先查找异或为1的节点,假设为第z个节点,之后,判断siz[z]是否大于等于k,如果满足条件,代表至少有z条路经过这个节点,如果小于k,那么我们需要寻找异或为0的这条路,但在选择这条路之前,我们需要将比他权重大的路径减去(异或为1),之后,进入下一个节点.
代码
千万要注意大小,节点数最少也得1e7+100,可以写成(5e5+100 )*32 ,因为范围问题改了好久,其次,注意1ll,注意左移右移时候,开long long
#include<iostream>#include<string>#include<queue>using namespace std;typedef long long ll;int n,k;const int N=5e5+100;ll siz[N*32];ll wsum[N];int tot;int tree[N*32][2];struct edge{ll wv;int x;int t; bool operator<(const edge&e)const{return wv<e.wv; }};bool cmp(edge e1,edge e2){if(e1.x!=e2.x)return e1.x<e2.x;else return e1.t<e2.t;}priority_queue<edge>pq;void insert(ll p){int fa=0;for(int i=32;i>=0;i--){ll w=(p>>i)&1;if(w==0){if(tree[fa][0]==0){tot++;tree[fa][0]=tot;}siz[tree[fa][0]]++;fa=tree[fa][0];}else{if(tree[fa][1]==0){tot++;tree[fa][1]=tot;}siz[tree[fa][1]]++;fa=tree[fa][1];}}}ll find(ll p,int t){int fa=0;ll res=0;for(int i=32;i>=0;i--){ll w=(p>>i)&1;if(w==0){if(siz[tree[fa][1]]>=t){res+=(1ll<<i);fa=tree[fa][1];}else{t-=siz[tree[fa][1]];fa=tree[fa][0];}}else{if(siz[tree[fa][0]]>=t){res+=(1ll<<i);fa=tree[fa][0];}else{t-=siz[tree[fa][0]];fa=tree[fa][1];}}}return res;}int main(void){cin>>n>>k;for(int i=1;i<=n;i++){cin>>wsum[i];wsum[i]^=wsum[i-1];}for(int i=0;i<=n;i++)insert(wsum[i]);k=k*2;for(int i=0;i<=n;i++){int p=i;int t=1;ll wv=find(wsum[i],1);pq.push({wv,p,t});}ll ans=0;for(int i=1;i<=k;i++){edge et=pq.top();ll wv=et.wv;int x=et.x;int t=et.t;t++; ans+=wv;pq.pop();wv=find(wsum[x],t);pq.push({wv,x,t});}cout<<ans/2;}