题解:P11761 [IAMOI R1] 明码标价
中位数二分来的实惠。
考虑小于等于
这有个很好的性质,就是一个这样的序列
考虑 dp。
设
转移考虑枚举
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[109];
int b[109];
int n;
__int128 dp[109][209];//第二维都多加了 100,这里的 dp[i][j] 表示实际上的 dp[i][j-100}
int check(){
dp[0][100]=1;
for(int i=1;i<=n;i++){
int sum;
sum=b[i];
for(int c=0;c<=200;c++){
dp[i][c]=0;
}
for(int j=i-1;j>=0;j--){
if(sum<=0){
for(int c=0;c<=200;c++){
dp[i][c]+=dp[j][c+1];
}
}else{
for(int c=1;c<=200;c++){
dp[i][c]+=dp[j][c-1];
}
}
sum+=b[j];
}
}
__int128 ans;
ans=0;
for(int i=0;i<=200;i++){
ans+=dp[n][i]*(i<=100?-1:1);
}
return (ans<=0?-1:1);
}
signed main(){
std::ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l,r;
l=1,r=1000000000;
while(l<r){
int mid;
mid=l+r;
mid>>=1;
for(int i=1;i<=n;i++){
b[i]=(a[i]<=mid?-1:1);
}
if(check()==-1){
r=mid;
}else{
l=mid+1;
}
}
cout<<l;
return 0;
}
更优解
注意到这个解法的性质过于优秀,我们尝试冲击平方。
回想一下我们 DP 有哪些比较普通的优化方法。
其中一个就是拿之前做的省掉一部分工序。
什么意思?就是转移可能有重复的,我们不想重复做。
我们试试?
注意到计数器
此时继续下去与此时的
我们可以直接省掉,把
当然别忘了特判
添加这条语句。
if(sum==0&&j){
for(int c=0;c<=200;c++){
dp[i][c]+=dp[j][c];
}
break;
}
虽然复杂度没变,但是代码却有了下限,如果
事实上我们增加这段后时间从
我们继续探索,我们发现如果计数器
而对于正负性相同的
所以可以直接前缀和优化这部分。
至于判断是正的还是负的,我们看
不过还有个难点就是如何确定在哪个位置
我们考虑
具体的,我们维护
为啥是前缀和?这个好维护。
然后在增加一个数增加多少,指针就增加多少。
然后算上自己就行。
如下所示,第一个是维护指针,第二个算自己的语句,实际写的时候二者并不会写在一起。
当然,细节较多,别忘了
zz+=b[i];//zz 表示指针
jj[zz]=i;
然后我们就得到了一个复杂度
#include<bits/stdc++.h>
using namespace std;
int a[109];
int b[109];
int n;
__int128 dp[109][209];
__int128 qz[109][209];
int jj[209];
int zz;
int check(){
dp[0][100]=1;
for(int i=0;i<=200;i++){
jj[i]=-1;//sum 取不到 0 的情况认为是 -1
}
jj[100]=0;
zz=100;
qz[0][100]=1;
for(int i=1;i<=n;i++){
zz+=b[i];
for(int c=0;c<=200;c++){
dp[i][c]=0;
}
if(jj[zz]==-1){
if(b[i]<=0){
for(int c=0;c<=200;c++){
dp[i][c]+=qz[i-1][c+1];
}
}else{
for(int c=1;c<=200;c++){
dp[i][c]+=qz[i-1][c-1];
}
}
}else{
if(b[i]<=0){
for(int c=0;c<=200;c++){
dp[i][c]+=qz[i-1][c+1]-qz[jj[zz]][c+1];
}
}else{
for(int c=1;c<=200;c++){
dp[i][c]+=qz[i-1][c-1]-qz[jj[zz]][c-1];
}
}
for(int c=0;c<=200;c++){
dp[i][c]+=dp[jj[zz]][c+1];
}
if(jj[zz]){
for(int c=0;c<=200;c++){
dp[i][c]+=dp[jj[zz]][c];
}
}
}
jj[zz]=i;
for(int c=0;c<=200;c++){
qz[i][c]=dp[i][c]+qz[i-1][c];
}
}
__int128 ans;
ans=0;
for(int i=0;i<=200;i++){
ans+=dp[n][i]*(i<=100?-1:1);
}
return (ans<=0?-1:1);
}
signed main(){
std::ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l,r;
l=1,r=1000000000;
while(l<r){
int mid;
mid=l+r;
mid>>=1;
for(int i=1;i<=n;i++){
b[i]=(a[i]<=mid?-1:1);
}
if(check()==-1){
r=mid;
}else{
l=mid+1;
}
}
cout<<l;
return 0;
}
这样我们的代码虽然常数很大,但是复杂度在那里,所以只需要跑
但是这道题还是受到整数类型影响,导致