ARC 066 传送门c++
若是存在可行方案则答案为$2^{n/2}$ide
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10,MOD=1e9+7; int n,x,res[MAXN]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); int rk=(n-1+x)/2; if(!res[rk+1]) res[rk+1]=i; else if(!res[n-rk]) res[n-rk]=i; else return puts("0"),0; } int res=1; for(int i=1;i<=n/2;i++) (res*=2)%=MOD; printf("%d",res); return 0; }
挺不错的一道数位$dp$spa
因为没法直接计算$sum$和$xor$的对数,所以考虑枚举$a,b$,而将$sum,xor\le n$做为限制条件3d
又由于公式:$a+b=aXORb+2*(a\&b)$,因此$a+b\le aXORb$,只考虑$a+b$的限制便可code
此时问题转化为对于每一个$sum\le n$求$xor$的取值个数blog
这样就能够用$dp[i][s]$表示前$i$位肯定,$a+b$的和为$s$的个数,每次分$a,b$在该位上总共有几个1转移get
(按每位1的个数转移才不会考虑异或与和同时相同的状况!)it
但这样复杂度是不对的,在枚举$s$上明显花费了没必要要的时间io
根据通常数位$dp$记录上限的思想,若是前$i$位的$n-s\ge 2$,这些数之后都保证合法,就能统一计算了event
这样就从$dp[i][s]$变成了$dp[i][0/1/2]$
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; #define MAX_D 64 #define MOD ((ll)1e9 + 7) ll N,dp[MAX_D][3],res;int nxt; int main() { scanf("%lld",&N); dp[MAX_D-1][0]=1; for(int i=MAX_D-1;i>0;i--) for(int j=0; j <= 2; j++) for(int k=0;k<=2;k++) { nxt=j*2+((N>>(i-1))&1)-k; if (nxt<0) continue; nxt=nxt>2?2:nxt; (dp[i-1][nxt]+=dp[i][j])%=MOD; } res=0; for (int i=0;i<=2;i++) (res+=dp[0][i])%=MOD; printf("%lld\n", res); return 0; }
从后往前用记忆化搜索的形式写起来更加方便
一开始将上限值就设为$n$,每次肯定最后一位取几个之后去掉最后一位,不用考虑和的合法性了
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MOD=1e9+7; map<ll,ll> dp;ll n; ll dfs(ll x) { if(dp.count(x)) return dp[x]; return dp[x]=(dfs(x>>1)+dfs((x-1)>>1)+dfs((x-2)>>1))%MOD; } int main() { scanf("%lld",&n); dp[0]=1;dp[1]=2; printf("%lld",dfs(n)); return 0; }
首先要观察出几点性质:
一、只有在减号后可能加括号
二、括号不可能嵌套超过两层,不然能够转化为只有两层的简化状况
这样就能够记录$dp[0/1/2]$分别表示当前还有几个左括号未匹配的最大值来$dp$了
#include <bits/stdc++.h> using namespace std; typedef long long ll; int n,x;char op;ll dp[3],nxt[3]; int main() { scanf("%d%d",&n,&dp[0]); dp[1]=dp[2]=-1ll<<60; for(int i=1;i<n;i++) { scanf(" %c%d",&op,&x); if(op=='-') x=-x; nxt[0]=dp[0]+x,nxt[1]=dp[1]-x,nxt[2]=dp[2]+x; dp[0]=max(nxt[0],max(nxt[1],nxt[2])); if(op=='+') dp[1]=max(nxt[1],nxt[2]),dp[2]=nxt[2]; else dp[1]=dp[0],dp[2]=max(nxt[1],nxt[2]); } printf("%lld",dp[0]); return 0; }
其实也能够不用$dp$,考虑若是在某个减号后加了第一个括号的最优解
发现此时能保证将下一个减号后的值都变为正贡献,但对当前位到下一个减号间的值是无能为力的
这样枚举第一个括号的位置对答案更新便可
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10; char op[MAXN]; int n,dat[MAXN],nxt[MAXN],fst; ll suf[MAXN],cur,res=-1ll<<60; int main() { scanf("%d%d",&n,&fst); for(int i=1;i<n;i++) scanf(" %c%d",&op[i],&dat[i]); cur=n; for(int i=n-1;i;i--) { suf[i]=suf[i+1]+dat[i]; if(op[i]=='-') nxt[i]=cur,cur=i; } cur=0; for(int i=1;i<n;i++) if(op[i]=='-') res=max(res,cur-suf[i]+2*suf[nxt[i]]),cur-=dat[i]; else cur+=dat[i]; res=max(res,cur); printf("%lld",res+fst); return 0; }