蒟蒻的 线性基 刷题记录
HDU3949 XOR
- 大意:给出n个数,求这些数通过xor能得到的第k小的值
- 做法:先高斯消元,完了之后回代,然后第k小,k用二进制表示,是1的那位就选,是0 的就不选
- 注意:给出的n个数能不能xor出0?因为题目是不允许不选,所以要考虑0的情况。
- 代码:
#include <bits/stdc++.h> #define nmax 10010 using namespace std; typedef long long ll; ll a[nmax],b[70],k[70]; //k[i] 第i小的数 int n,q; int main(){ int T; cin>>T; for (int cas=1; cas<=T; cas++) { memset(b,0,sizeof(b)); memset(k,0,sizeof(k)); printf("Case #%d:\n",cas); scanf("%d",&n); for (int i=0; i<n; i++) { scanf("%lld",&a[i]); //求线性基 for (int j=61; j>=0; j--) { if(a[i]&(1LL<<j)){ if(b[j]) a[i]^=b[j]; else { b[j]=a[i]; break; } } } } //往上回代,顺便统计个数 int cnt=0; for (int i=0; i<=61; i++) if(b[i]) { k[cnt]=b[i]; cnt++; for (int j=i+1; j<=61; j++) if(b[j]&(1LL<<i)) b[j]^=b[i]; } ll mmax=(1LL<<cnt)-1; scanf("%d",&q); //cout<<"q= "<<q<<endl; ll inq; bool flag=false;//特别判断0 if(cnt<n) flag=true; for (int j=0; j<q; j++) { // cout<<"quq"<<endl; scanf("%lld",&inq); if(flag) inq--; //也是判断0的内容 if(inq>mmax) cout<<-1<<endl; else { ll ans=0; for (int i=0; (1LL<<i)<=inq; i++) if((1LL<<i)&inq) ans^=k[i]; printf("%lld\n",ans); } } } return 0; }
BZOJ2460 元素
- 做法:贪心,按magic值从大到小排序求线性基
- 代码:
#include <bits/stdc++.h> #define nmax 1010 using namespace std; typedef long long ll; struct stone{ ll num,mac; bool operator < (const stone a) const { return a.mac<mac; } }st[nmax]; int n; ll b[70]={0}; int main(){ cin>>n; for (int i=0; i<n; i++) scanf("%lld%lld",&st[i].num,&st[i].mac); sort(st,st+n); //for (int i=0; i<n; i++) cout<<st[i].num<<' '<<st[i].mac<<endl; ll ans=0; for (int i=0; i<n; i++) for (int j=63; j>=0; j--) if(st[i].num&(1LL<<j)){ if(b[j]) st[i].num^=b[j]; else{ b[j]=st[i].num; ans+=st[i].mac; break; } } printf("%lld\n",ans); return 0; }
BZOJ2115 XOR
- 做法:dfs先把图拉成一棵树,然后看有没有倒回去的边(类似tarjan那种)处理出所有的环的xor值,然后和1~ndfs树上那个值做线性基求出ans
- 注意:wa的地方有两点,一个是处理环的时候,是不是环要看是不是dfs中“指回去”,用一个数组记录dfs的顺序。另一个是求最大xor值时。1~n那条路不要加到线性基里去,因为那个数是必须要取的,加到线性基里后面求最大值的时候可能就不取它了。
- 代码:
1 #include <bits/stdc++.h> 2 #define nmax 50010 3 #define mmax 100010 4 5 using namespace std; 6 typedef long long ll; 7 int head[nmax]={0},d[nmax]={0}; //被vis到的顺序 8 ll a[nmax+mmax],tmp[nmax],b[70]={0}; 9 int n,m,idx=0,idxa=0; //idza是记录环的数量 10 struct edge{ 11 int v,ne; 12 ll w; 13 }e[mmax*2]; 14 15 inline void addedge(int u,int v,ll w){ 16 idx++; 17 e[idx].v=v; 18 e[idx].w=w; 19 e[idx].ne=head[u]; 20 head[u]=idx; 21 } 22 23 void build(){ 24 cin>>n>>m; 25 int a,b; ll c; 26 for (int i=0; i<m; i++) { 27 scanf("%d%d%lld",&a,&b,&c); 28 addedge(a,b,c); 29 addedge(b,a,c); 30 } 31 } 32 33 void dfs(int u,int cnt){ 34 d[u]=cnt; 35 for (int i=head[u]; i; i=e[i].ne){ 36 int v=e[i].v; ll w=e[i].w; 37 if(d[v]>d[u]) continue; 38 if(d[v]){ 39 ll x=tmp[v]^tmp[u]^w; //这个环的xor值 40 if(x) a[++idxa]=x; //如果是0就舍弃 41 }else { 42 tmp[v]=tmp[u]^w; //在dfs(u)之前,u的tmp值已经算出 43 dfs(v,cnt+1); 44 } 45 } 46 } 47 48 void xxj(){ 49 for (int i=1; i<=idxa; i++) for (int j=63; j>=0; j--) if((1LL<<j)&a[i]){ 50 if(b[j]) a[i]^=b[j]; 51 else { b[j]=a[i]; break; } 52 } 53 ll ans=tmp[n]; 54 for (int i=63; i>=0; i--) ans=max(ans,ans^b[i]); 55 printf("%lld\n",ans); 56 } 57 58 int main(){ 59 build(); 60 dfs(1,1); 61 xxj(); 62 return 0; 63 }
BZOJ2844: albus就是要第一个出场
- 对于给定的大小为N数列a,它的子集有2^n个,每个子集可以异或的所有的数可以用线性基表示
- 设线性基有b个,然后不在线性基里的数有x=n-b个
- 可以知道通过a任意异或出的所有数是这样一个情况:一共有2^b个不同的数,每个数出现的次数相同为2^x
- 证明:把a分成两份,线性基的一份和不在线性基的一份,然后在不在线性基的那一份里随便取一种排列并把它们异或,设结果为v,v用线性基那一份也可以造出来,然后v^v=0,然后再用线性基造一遍所有的数,这些数就又全部出现了一次,这样搞可以让这些数全部出现2^x次
- 代码:
#include <bits/stdc++.h> #define nmax 100010 #define mod 10086 using namespace std; typedef long long ll; ll a[nmax],b[60]; ll n,x; ll fastpow(ll x){ ll ta=2,ans=1; for (ll i=1; i<=x; i<<=1){ if(x&i) ans=(ans*ta)%mod; ta=(ta*ta)%mod; } return ans; } int main(){ scanf("%lld",&n); for (ll i=0; i<n; i++) { scanf("%lld",&a[i]); for (ll j=40; j>=0; j--){ if( (1LL<<j)&a[i] ) if(b[j]) a[i]^=b[j]; else { b[j]=a[i]; break; } } } scanf("%lld",&x); int k=0,cnt=0; for (ll i=0; i<=40; i++) { if(b[i]) { if( x & (1<<i) ) k+=(1LL<<cnt);//注意这里加的不是b[i]。。 cnt++; } } cout<<k*fastpow(n-cnt)%mod+1<<endl; return 0; }