【线性基】 Hdu Operation+牛客 xor序列+ CQOI2013新NIM游戏

线性基

性质:

(1):原序列里面的任意一个数都可以由线性基里面的一些数异或得到。
(2):线性基里面的任意一些数异或起来都不能得到0
(3):线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的
 


先放模板:

插入函数

void insert_(ll x)
{
    for(int i=60;i>=0;i--){
        if((x>>i)&1)
        {
            if(d[i])    x^=d[i];
            else{
                d[i]=x;
                break;
            }
        }
    }
}

查询第k小的异或值

void work()//处理线性基
{
	for(int i=1;i<=60;i++)
	for(int j=1;j<=i;j++)
	if(d[i]&(1<<(j-1)))d[i]^=d[j-1];
}
ll k_th(ll k)
{
	if(k==1&&tot<n)return 0;//特判一下,假如k=1,并且原来的序列可以异或出0,就要返回0,tot表示线性基中的元素个数,n表示序列长度
	if(tot<n)k--;//类似上面,去掉0的情况,因为线性基中只能异或出不为0的解
	work();
	ll ans=0;
	for(int i=0;i<=60;i++)
	if(d[i]!=0)
	{
		if(k%2==1)ans^=d[i];
		k/=2;
	}
}

 

 

 


Operation

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 3129    Accepted Submission(s): 972


 

Problem Description

There is an integer sequence a of length n and there are two kinds of operations:

  • 0 l r: select some numbers from al...ar so that their xor sum is maximum, and print the maximum value.
  • 1 x: append x to the end of the sequence and let n=n+1.

 

 

 

Input

There are multiple test cases. The first line of input contains an integer T(T≤10), indicating the number of test cases.
For each test case: 
The first line contains two integers n,m(1≤n≤5×105,1≤m≤5×105), the number of integers initially in the sequence and the number of operations.
The second line contains n integers a1,a2,...,an(0≤ai<230), denoting the initial sequence.
Each of the next m lines contains one of the operations given above.
It's guaranteed that ∑n≤106,∑m≤106,0≤x<230.
And operations will be encrypted. You need to decode the operations as follows, where lastans denotes the answer to the last type 0 operation and is initially zero: 
For every type 0 operation, let l=(l xor lastans)mod n + 1, r=(r xor lastans)mod n + 1, and then swap(l, r) if l>r.
For every type 1 operation, let x=x xor lastans.

 

Output

For each type 0 operation, please output the maximum xor sum in a single line.

 

Sample Input

1 3 3 0 1 2 0 1 1 1 3 0 3 4

 

Sample Output

1 3

 

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<map>
#include<set>
#include<stack>
#define debug cout<<"*"<<endl;
#define input(x) scanf("%d",&x)
#define output(x) printf("%d\n",x);
#define llinput(x) scanf("%lld",&x)
#define lloutput(x) printf("%lld\n",x);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<string,int> psi;
typedef pair<char,char> pcc;
const int mod=998244353;
const int INF=INT_MAX;
const long long LLINF=LONG_MAX;
const int maxn=5e5+7;
const double PI=acos(-1);
const int N=31;
int a[maxn],pos[maxn][N+7],val[maxn][N+7];
void Init(int index)
{
    for(int i=0;i<=N;i++)
    {
        pos[index][i]=pos[index-1][i];
        val[index][i]=val[index-1][i];
    }
}
void insert_(int x,int index)
{
    int tmp=index;
    Init(index);
    for(int i=N;i>=0;--i)
    {
        if(x&(1<<i))
        {
            if(val[index][i]==0)
            {
                val[index][i]=x;
                pos[index][i]=tmp;
                break;
            }
            if(pos[index][i]<tmp)
            {
                swap(val[index][i],x);
                swap(pos[index][i],tmp);
            }
            x^=val[index][i];
        }
    }
}
int query(int l,int r)
{
    int maxx=0;
    for(int i=N;i>=0;--i)
    {
        if(pos[r][i]>=l)
        {
            maxx=max(maxx,maxx^val[r][i]);
        }
    }
    return maxx;
}
int main()
{
    int t;
    input(t);
    while(t--){
    int n,m;
    input(n);
    input(m);
    for(int i=1;i<=n;i++)
    {
        input(a[i]);
        insert_(a[i],i);
    }
    int ans=0;
    while(m--)
    {
        int oper;
        input(oper);
        if(oper&1)
        {
            input(a[++n]);
            a[n]^=ans;
            insert_(a[n],n);
        }
        else{
            int l,r;
            input(l);
            input(r);
            l=(l^ans)%n+1;
            r=(r^ans)%n+1;
            if(l>r)
                swap(l,r);
            ans=query(l,r);
            output(ans);
        }
    }
    }
    return 0;
}

 


 

链接:https://ac.nowcoder.com/acm/problem/19928
来源:牛客网
 

[CQOI2013]新NIM游戏

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。 

本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。 如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。  

 

链接:https://ac.nowcoder.com/acm/problem/19928
来源:牛客网
 

输入描述:

 

第一行为整数k。即火柴堆数。

第二行包含k个不超过109的正整数,即各堆的火柴个数。  

输出描述:

输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。

示例1

输入

复制

6
5 5 6 6 5 5

输出

复制

21

 

分析:

根据尼姆博弈,堆数的火柴数量异或值为0为必输态

第一轮拿完后,剩下的火柴不存在一个子集,使得子集异或值为0

从第三轮开始为正常的Nim博弈

这道题没有-1的情况是因为先手可以把n-1堆拿走,剩下一堆后手只能干瞪眼,所以先手必赢

题目要求拿的火柴数目尽可能少,线性基内放的是大的值,所以利用贪心思想,从大到小建线性基,若当前值是可以通过线性基中若干个元素异或得到,则可以把这一堆拿出,使最后剩下的是异或值不为0

动态维护线性基判断即可

 

#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<map>
#include<set>
#include<stack>
#define debug cout<<"*"<<endl;
#define input(x) scanf("%d",&x)
#define output(x) printf("%d\n",x);
#define llinput(x) scanf("%lld",&x)
#define lloutput(x) printf("%lld\n",x);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<string,int> psi;
typedef pair<char,char> pcc;
const int mod=998244353;
const int INF=1e9+7;
const int maxn=1e5+7;
const double PI=acos(-1);
const int N=3007;
ll d[maxn];
ll ans=0;
int a[maxn];
bool cmp(ll a,ll b){return a>b;}
bool insert_(int x)
{
    for(int i=60;i>=1;--i)
    {
        if((x>>i)&1)
        {
            if(d[i])    x^=d[i];
            else{
                d[i]=x;
                break;
            }
        }
    }
    return x>0;
}
int main()
{
    int k;
    input(k);
    ll sum=0;
    for(int i=1;i<=k;++i)
    {
        cin>>a[i];
        sum+=a[i];
    }
    sort(a+1,a+k+1,cmp);
        ans=0;
        for(int i=1;i<=k;++i)
        {
            if(!insert_(a[i]))
            {
                ans+=a[i];
            }
        }
        if(ans==sum)
        {
            ans=-1;
        }
        lloutput(ans);
    return 0;
}

 


xor序列

链接:https://ac.nowcoder.com/acm/problem/17968
来源:牛客网
 

小a有n个数,他提出了一个很有意思的问题:他想知道对于任意的x, y,能否将x与这n个数中的任意多个数异或任意多次后变为y

 

输入描述:

第一行为一个整数n,表示元素个数
第二行一行包含n个整数,分别代表序列中的元素
第三行为一个整数Q,表示询问次数
接下来Q行,每行两个数x,y,含义如题所示

输出描述:

输出Q行,若x可以变换为y,输出“YES”,否则输出“NO”

示例1

输入

复制

5
1 2 3 4 5
3
6 7 
2 1
3 8

输出

复制

YES
YES
NO

说明

对于(6,7)来说,6可以先和3异或,再和2异或
对于(2,1)来说,2可以和3异或
对于(3,8)来说,3不论如何都不能变换为8

备注:

对于100%的数据,n,Q<=105
保证所有运算均在int范围内

 

分析:

求出已知序列线性基,对每一对x,y判断x^y是否可以加入线性基内即可

 

#include<bits/stdc++.h>
using namespace std;
const int INF=INT_MAX;
const long long LLINF=LONG_MAX;
const int mod=1e6+3;
const int xmax=1e5+7;
typedef long long ll;
ll a;
ll d[100007];
bool sign=false;
void insert_(ll x)
{
    for(int i=60;i>=0;i--){
        if((x>>i)&1)
        {
            if(d[i])    x^=d[i];
            else{
                d[i]=x;
                break;
            }
        }
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a;
        insert_(a);
    }
    ll q;
    cin>>q;
    while(q--)
    {
        ll x,y;
        cin>>x>>y;
        x^=y;
        for(int i=60;i>=0;i--)
        {
            if(x&(1ll<<i)){
                if(d[i])    x^=d[i];
                else    break;
            }
        }
        if(x)
            cout<<"NO"<<endl;
        else
            cout<<"YES"<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值