牛客網 Wannafly挑戰賽14 E.無效位置 (並查集+線性基)
阿新 • • 發佈:2018-12-16
題目描述
給一個1-base陣列{a},有N次操作,每次操作會使一個位置無效。一個區間的權值定義為這個區間裡選出一些數的異或和的最大值。求在每次操作前,所有不包含無效位置的區間的權值的最大值。
輸入描述:
第一行讀入一個正整數(1 <= n <= 105)
第二行讀入n個正整數,第i個表示a[i](0<= a[i] <= 109)
第三行讀入n個正整數,第i個表示x[i]即第i次操作的位置,保證x[i]互不相同。
輸出描述:
輸出n行答案
示例1
輸入
10 169 816 709 896 58 490 97 254 99 796 4 2 3 10 5 6 1 8 9 7
輸出
1023 994 994 994 490 490 254 254 99 97
題目大意:給定n個數的陣列,然後每次刪除一個位置,該位置就會變為無效位置,詢問本次操作之前的沒有無效位置的區間異或最大值。
題目思路:異或最大值,容易想到線性基,然後就是維護區間刪數,不是很好刪,所以我們就離線從後往前新增數,並查集,在新增一個數的時候,檢視其左右點是否有值,兩邊都有則需要區間合併,兩邊都沒有則插入自己區間,否則插入某一邊。
AC程式碼:
#include<cstdio> #include<cmath> #include<cstring> #include<string> #include<cstdlib> #include<algorithm> #include<iostream> #include<queue> #include<stack> #include<map> using namespace std; #define FOU(i,x,y) for(int i=x;i<=y;i++) #define FOD(i,x,y) for(int i=x;i>=y;i--) #define MEM(a,val) memset(a,val,sizeof(a)) #define PI acos(-1.0) const double EXP = 1e-9; typedef long long ll; typedef unsigned long long ull; const int INF = 0x3f3f3f3f; const ll MINF = 0x3f3f3f3f3f3f3f3f; const double DINF = 0xffffffffffff; const int mod = 1e9+7; const int N = 1e5+5; //線性基 struct L_B{ ll d[63],new_d[63]; //d陣列是第一次線性基,new_d是用於求Kth的線性基 int cnt; //記錄個數 L_B(){ memset(d,0,sizeof(d)); memset(new_d,0,sizeof(new_d)); cnt=0; } void clear(){ memset(d,0,sizeof(d)); memset(new_d,0,sizeof(new_d)); cnt=0; } bool ins(ll val){ for(int i=62;i>=0;i--){ if(val&(1ll<<i)){ //存在貢獻則繼續 if(!d[i]){ //線性基不存在,選入線性基中 d[i]=val; break; } val^=d[i]; //否則直接改變其值 } } return val>0; //大於0則是成功加入線性基的向量 } ll query_max(){ ll ans=0; for(int i=62;i>=0;i--) if((ans^d[i])>ans) //能讓值變大則選入 ans^=d[i]; return ans; } ll query_min(){ for(int i=0;i<=62;i++) if(d[i]) //最小異或值 return d[i]; return 0; } //以下程式碼為求第k大異或值,其中cnt用於判斷是否可以取到0 // cnt==n(數的個數)則不可以取到0,第k小就是第k小,否則第k小是第k-1小 void rebuild() { for(int i=62;i>=0;i--) for(int j=i-1;j>=0;j--) if (d[i]&(1LL<<j)) d[i]^=d[j]; for (int i=0;i<=62;i++) if (d[i]) new_d[cnt++]=d[i]; } ll kthquery(int k) { ll ans=0; if (k>=(1ll<<cnt)) return -1; for (int i=62;i>=0;i--) if (k&(1ll<<i)) ans^=new_d[i]; return ans; } }; //線性基合併,暴力合併 L_B merge(const L_B &n1,const L_B &n2) { L_B ret=n1; for (int i=62;i>=0;i--) if (n2.d[i]) ret.ins(n2.d[i]); return ret; } int pre[N]; int Find(int x){ int p=x,tmp; while(x!=pre[x]) x=pre[x]; while(p!=x){ tmp = pre[p]; pre[p] = x; p = tmp; } return x; } void join(int x,int y){ int p=Find(x); int q=Find(y); if(p!=q) pre[q]=p; } ll ans[N]; ll a[N]; int q[N]; L_B lb[N]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&q[i]); MEM(pre,0); ll maxx=0; for(int i=n;i>=1;i--){ int id = q[i]; pre[id]=id; if(pre[id-1]!=0&&pre[id+1]!=0){ int tmp = Find(id-1); lb[tmp].ins(a[id]); join(id-1,id); int tmp1 = Find(id+1); join(id,id+1); int tmp2 = Find(id); lb[tmp2] = merge(lb[tmp2],lb[tmp1]); } else if(pre[id-1]!=0&&pre[id+1]==0){ int tmp = Find(id-1); lb[tmp].ins(a[id]); join(id-1,id); } else if(pre[id-1]==0&&pre[id+1]!=0){ int tmp = Find(id+1); join(id+1,id); lb[tmp].ins(a[id]); } else{ lb[id].ins(a[id]); } int tmp = Find(id); maxx=max(maxx,lb[tmp].query_max()); ans[i] = maxx; } for(int i=1;i<=n;i++) printf("%lld\n",ans[i]); return 0; }