23 1 21 1 13 0 31 1 1 Sample Output21 Author金策工业综合大学(DPRK) Source2016 Multi-University Training Contest 9 题意:给你n个数字的序列,问你最多能把序列分成多少份,每份长度不能超过l,且异或和不能超过x。
题解:
可以想到n2的dp[n][m]表示到第n个数字能否分成m段然后二分,但是这题不管是时间还是空间都过不去,而n2的dp仅仅记录了这个状态能否到达,有些浪费 所以我们可以改成dp[n]=m表示到第n的数组,最多能分成多少段 dp[i]=max(dp[j])+1,并且s[i]XORs[j]<=x 这里可以用trie树进行优化。
维护前缀异或和,构造trie,节点保留这个子树下的最大值,每次插入和删除都要更新,然后每次去字典树里查最大值更新即可。注意i-l-1>= 0时要删除一个前缀sum[i-l-1].
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<vector>#include<queue>#include<stack>using namespace std;#define rep(i,a,n) for (int i=a;i<n;i++)#define per(i,a,n) for (int i=n-1;i>=a;i--)#define pb push_back#define fi first#define se secondtypedef vector<int> VI;typedef long long ll;typedef pair<int,int> PII;const int inf=0x3fffffff;const int mod=268435456;const int maxn=1e5+100;int ch[32*maxn][3];int d[maxn],val[32*maxn],num[32*maxn];int sz;int x;ll sum[maxn];void insert(int i,int id,int u){ if(i==-1) { val[u]=d[id]; return; } ll x=sum[id]; int v=(x>>i)&1; if(!ch[u][v]) { ch[u][v]=++sz; num[sz]=0; val[sz]=-1; } num[ch[u][v]]++; insert(i-1,id,ch[u][v]); val[u]=max(val[u],val[ch[u][v]]);}void del(int i,int id,int u){ if(i==-1) { if(!num[u]) val[u]=-1; return; } ll x=sum[id]; int v=(x>>i)&1; //一开始写成v=x&(1<<i),re了好久,这简直是个傻逼错误。。。 num[ch[u][v]]--; del(i-1,id,ch[u][v]); val[u]=val[ch[u][v]]; if(ch[u][v^1]&&num[ch[u][v^1]]) val[u]=max(val[ch[u][v^1]],val[ch[u][v]]);}int query(int i,ll c,int u){ if(i==-1) { return val[u]; } int v=(c>>i)&1,d=(x>>i)&1; int ans=-1; if(d==1) { if(ch[u][v]&&num[ch[u][v]]) ans=max(ans,val[ch[u][v]]); if(ch[u][v^1]&&num[ch[u][v^1]]) ans=max(ans,query(i-1,c,ch[u][v^1])); } else { if(ch[u][v]&&num[ch[u][v]]) ans=max(ans,query(i-1,c,ch[u][v])); } return ans;}int main(){ int cas; scanf("%d",&cas); while(cas--) { memset(d,0,sizeof(d)); memset(val,-1,sizeof(val)); memset(ch,0,sizeof(ch)); memset(num,0,sizeof(num)); sz=0; int n,l; ll a,p,q; scanf("%d%d%d%lld%lld%lld",&n,&x,&l,&a,&p,&q); sum[1]=a;sum[0]=0; rep(i,2,n+1) { a=(1ll*a*p%mod+q)%mod; sum[i]=sum[i-1]^a; } d[0]=0; insert(30,0,0); rep(i,1,n+1) { if(i>l&&d[i-l-1]) del(30,i-l-1,0); if(i==l+1) del(30,0,0); int p=query(30,sum[i],0); if(p>=0) { d[i]=p+1; insert(30,i,0); } } printf("%d/n",d[n]); } return 0;}
可以想到n2的dp[n][m]表示到第n个数字能否分成m段然后二分,但是这题不管是时间还是空间都过不去,而n2的dp仅仅记录了这个状态能否到达
新闻热点
疑难解答