题目
长度为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;
}
动态规划解决序列异或问题
博客内容涉及使用动态规划解决序列中严格上升子序列异或值的计数与求解问题。对于不同规模的数据范围,分别给出了easy和hard两种复杂度的解决方案,通过维护可达集或状态转移矩阵来优化计算。核心思想在于利用序列特性优化异或操作,减少计算复杂度。
1294

被折叠的 条评论
为什么被折叠?



