标签:不能 return long 区间 max 子序列 ++i its 括号
E 题意:给你一个长度为n的数组和k,问你这个数组的所有长度为k的子序列的和的平方的异或和是多少,数据保证Cnk<=1e5,n-1e5;
题解:搜索,直接跳过0节点就可以了
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+88; int n,k; long long sum,ans; int a[N],f; void dfs(int u,int ow,int now){ if(ow==k) { if(!f) ans^=1LL*now*now; else ans^=1LL*(sum-now)*(sum-now); return; } for(int i=u;i<=n;++i) dfs(i+1,ow+1,now+a[i]); } int main(){ int T; for(scanf("%d",&T);T--;){ scanf("%d%d",&n,&k); sum=f=ans=0; for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<=n;++i) sum+=a[i]; if(k>n-k) f=1,k=n-k; dfs(1,0,0); printf("%lld\n",ans); } }
H:
题意:给你一个长度为5e5的括号序列,只包含左括号和右括号,有m次操作(m<=5e5)
操作1是将括号反向,操作2则是问以x开头的合法序列的最长长度是多少。
题解:若一个序列A[i....k]合法,则必有i-k中左括号的数目>=右括号并且在k处左括号与右括号相等。
转化成数字就是左括号的权值是1,右括号的权值是-1,那么从i开始到j的权值和一定要大于等于i并且从i到k的权值和等于i。
则就是和区间最小值的有关的问题了,想到线段树维护这个权值前缀和,然后查询的时候,第一感觉肯定是查前缀和值等于起始处的,但是这样是不可行的,因为你二分查找的时候,如果这个区间最小值等于起始处的时候,无法判断是向左还是向右。
那么就分步进行,首先从左开始找到第一个小于起始值的,这个直接二分就可以找了,找到的话那么答案就有了。如果没有找到,那么就从序列末尾找等于起始值的,找到就是答案。
两次二分的差别就在于第一次是毫无目的性,你无法判断向右还是向左,而第二次就很明朗了。
#include<bits/stdc++.h> using namespace std; const int N=5e5+88; int mi[N<<2],lazy[N<<2],a[N],n,m; char s[N]; void build(int l,int r,int t){ lazy[t]=0; if(l==r) { mi[t]=a[l]; return; } int m=(l+r)>>1; build(l,m,t<<1); build(m+1,r,t<<1|1); mi[t]=min(mi[t<<1],mi[t<<1|1]); } void pw(int t){ if(lazy[t]) { mi[t<<1]+=lazy[t]; mi[t<<1|1]+=lazy[t]; lazy[t<<1]+=lazy[t]; lazy[t<<1|1]+=lazy[t]; lazy[t]=0; } } int query(int l,int r,int L,int R,int t){ if(!l) return 0; if(r>=R&&l<=L) return mi[t]; pw(t); int m=(L+R)>>1; if(r<=m) return query(l,r,L,m,t<<1); else if(l>m) return query(l,r,m+1,R,t<<1|1); else { int t1=query(l,r,L,m,t<<1),t2=query(l,r,m+1,R,t<<1|1); return min(t1,t2); } } void update(int l,int L,int R,int t,int val){ if(L>=l) { mi[t]+=val; lazy[t]+=val; return; } pw(t); int m=(L+R)>>1; if(l<=m) update(l,L,m,t<<1,val); update(l,m+1,R,t<<1|1,val); mi[t]=min(mi[t<<1],mi[t<<1|1]); } int main(){ int T,op,x; for(scanf("%d",&T);T--;){ scanf("%d%d",&n,&m); scanf("%s",s+1); for(int i=1;s[i];++i) a[i]=a[i-1]+(s[i]==‘(‘?1:-1); build(1,n,1); for(int i=1;i<=m;++i) { scanf("%d%d",&op,&x); if(op==1) { if(s[x]==‘(‘) s[x]=‘)‘,update(x,1,n,1,-2); else s[x]=‘(‘,update(x,1,n,1,2); } else { int xx=-2,tar=query(x-1,x-1,1,n,1),l=x,r=n,ans=x-1; int tc=query(x,n,1,n,1); if(tc<tar){ while(l<=r){ int m=(l+r)>>1; int tc=query(l,m,1,n,1); if(tc<tar) { xx=m; r=m-1; } else l=m+1; } printf("%d\n",xx-x); } else { l=x,r=n; while(l<=r){ int m=(l+r)>>1; int tc=query(m,r,1,n,1); if(tc==tar){ ans=m; l=m+1; } else r=m-1; } printf("%d\n",ans-x+1); } } } } }
K:题意:给你一个n,m,n<=1000,m<=100.
长度为n的序列相对位置不能变,长度为m的任意插入长度为n的序列之中去,插入过程由你决定,然后相邻的值不能选,问你能选的最大值是多少。
题解:不插入的话直接dp[n][2]推过去就行了,有插入的话就相当于每段之间多了m*m个状态,多的就是目前B序列的情况。然后也是顺推就行了。
两种写法,一种无状态重叠,另一种有。
状态重叠的关键就是用上一次选的代替这一次选的是对答案没有影响的。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e3+55; const int M=1e2+55; int sum[N],f[2][M][M],g[2][M][M]; int a[N],b[N],n,m; int main(){ int T; for(scanf("%d",&T);T--;){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<=m;++i) scanf("%d",b+i); for(int i=0;i<2;++i) for(int j=0;j<=m;++j) for(int k=0;k<=m;++k) f[i][j][k]=g[i][j][k]=-1000000000; g[0][0][0]=0; for(int i=0;i<=n;++i) for(int j=0;j<=m;++j) for(int k=0;k<=m-j;++k) { g[(i+1)&1][j][k]=max(f[i&1][j][k],g[(i+1)&1][j][k]); g[i&1][j+1][k]=max(f[i&1][j][k],g[i&1][j+1][k]); f[(i+1)&1][j][k]=max(g[i&1][j][k]+a[i+1],f[(i+1)&1][j][k]); f[(i+1)&1][j][k]=max(f[i&1][j][k],f[(i+1)&1][j][k]); f[i&1][j][k+1]=max(g[i&1][j][k],f[i&1][j][k+1]); } sort(b+1,b+m+1); for(int i=1;i<=m;++i) sum[i]=b[i]+sum[i-1]; int ans=0; for(int i=0;i<=m;++i) { ans=max(ans,f[n&1][i][m-i]+sum[m]-sum[i]); ans=max(ans,g[n&1][i][m-i]+sum[m]-sum[i]); } printf("%d\n",ans); } }
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e3+55; const int M=1e2+55; int sum[N],f[2][M][M],g[2][M][M]; int a[N],b[N],n,m; int main(){ int T; for(scanf("%d",&T);T--;){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<=m;++i) scanf("%d",b+i); for(int i=0;i<2;++i) for(int j=0;j<=m;++j) for(int k=0;k<=m;++k) f[i][j][k]=g[i][j][k]=-1000000000; g[0][0][0]=0; for(int i=0;i<=n;++i) for(int j=0;j<=m;++j) for(int k=0;k<=m-j;++k) { g[(i+1)&1][j][k]=max(f[i&1][j][k],g[(i+1)&1][j][k]); g[(i+1)&1][j][k]=max(g[i&1][j][k],g[(i+1)&1][j][k]); g[i&1][j+1][k]=max(f[i&1][j][k],g[i&1][j+1][k]); g[i&1][j+1][k]=max(g[i&1][j][k],g[i&1][j+1][k]); f[(i+1)&1][j][k]=max(g[i&1][j][k]+a[i+1],f[(i+1)&1][j][k]); f[i&1][j][k+1]=max(g[i&1][j][k],f[i&1][j][k+1]); } sort(b+1,b+m+1); for(int i=1;i<=m;++i) sum[i]=b[i]+sum[i-1]; int ans=0; for(int i=0;i<=m;++i) { ans=max(ans,f[n&1][i][m-i]+sum[m]-sum[i]); ans=max(ans,g[n&1][i][m-i]+sum[m]-sum[i]); } printf("%d\n",ans); } }
标签:不能 return long 区间 max 子序列 ++i its 括号
原文地址:https://www.cnblogs.com/mfys/p/8979737.html