Codeforces Round #750 (Div. 2) F.Korney Korneevich and XOR (easy&&hard version)(dp)

题目

长度为n的序列,第i个值为ai,

easy:n<=1e5,0<=ai<=500

hard:n<=1e6,0<=ai<=5000

对于序列中的所有严格上升子序列,

每一种序列的值异或起来,得到一个值v,

输出v的种类数,按增序输出所有v的可能性

思路来源

quality代码(hard)

题解

easy:

暴力bitset维护可达集,每次暴力异或,1e5*500*500/64

dp[i]表示当前能异或出i的序列最小值

hard:

dp[i][j]表示当前序列最尾为i时,子序列能异或出j时,子序列末尾值i的最小位置

实际实现时,由于需要根据前缀转移,实际需要不超过i

心得

感觉还是用到了严格上升子序列的本质,

要么是子序列最后一个值尽可能小,

要么是达到相同的值的时候位置尽可能靠前

代码1(easy)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring> 
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int N=515,INF=0x3f3f3f3f;
int ans[N],c;
int n,now[N],v;
int main(){
    memset(now,INF,sizeof now);
    now[0]=0;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&v);
        for(int j=0;j<512;++j){
            if(now[j]<v){
                now[j^v]=min(now[j^v],v);
            }
        }
    }
    for(int j=0;j<512;++j){
        if(now[j]<INF){
            ans[++c]=j;
        }
    }
    printf("%d\n",c);
    for(int i=1;i<=c;++i){
        printf("%d%c",ans[i]," \n"[i==c]);
    }
    return 0;
}

代码2(hard)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring> 
#include <cmath>
#include <vector>
#include <bitset>
using namespace std;
const int N=5e3+1,M=8192,INF=0x3f3f3f3f;
//dp[i][j]:子序列尾<=i时, 能异或出j的最小的序列位置
int n,v,dp[N][M],ans[M],c;
vector<int>pos[N];
int main(){
    memset(dp,INF,sizeof dp);
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&v);
        pos[v].push_back(i);
    }
    dp[0][0]=0;
    for(int i=1;i<N;++i){
        for(int j=0;j<M;++j){
            dp[i][j]=dp[i-1][j];
            if(pos[i].empty())continue;
            if(i==j){
                dp[i][j]=min(dp[i][j],pos[i].front());
            }
            int id=upper_bound(pos[i].begin(),pos[i].end(),dp[i-1][j^i])-pos[i].begin();
            if(id!=pos[i].end()-pos[i].begin()){
                dp[i][j]=min(dp[i][j],pos[i][id]);
            }
        }
    }
    for(int j=0;j<M;++j){
        if(dp[N-1][j]!=INF){
            ans[++c]=j;
        }
    }
    printf("%d\n",c);
    for(int i=1;i<=c;++i){
        printf("%d%c",ans[i]," \n"[i==c]);
    }
	return 0;
} 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值