NOIP模拟赛总结
区间减转化为差分数组两端点的操作。\(c_i=a_i-a_{i-1}\),尽量挑一正一负的 \(c_i\) 配对可以得到最小次数。
考虑怎么得到 \(min/max\) 值,为了区间长度尽可能小,选择最近的匹配点配对。反之则选最远的配对。
但是第二问有可能出现匹配不成功的情况,例如 114514 差分数组 1 0 3 1 -4 3 -4,前两个13先和-4匹配,后面出现剩余情况。这是因为后一个贪心急着往后匹配而忽略了中间匹配。
像这样的实现就只能对第一问而不是第二问。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,l,r) for(int i=l;i<=r;i++)
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define pb push_back
int n;
const int N=1e6+10;
int a[N];
int c[N],d[N];
priority_queue<pii,vector<pii>,greater<pii> >q;
priority_queue<pii,vector<pii>,greater<pii> >q2;
int ans;
signed main(){
freopen("sequence4.in","r",stdin);
freopen("sequence4.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
For(i,1,n+1){
if(i<=n)cin>>a[i];
c[i]=a[i]-a[i-1];
d[i]=a[i]-a[i-1];
if(c[i]<0)q.push(mp(i,c[i]));
}
//-1: c[l]-- c[r+1]++
// (>0) (<0)
For(i,1,n){
if(c[i]<=0)continue;
while(!q.empty()){
int id=q.top().fi;
// cout<<id<<"\n";
if(id<=i)q.pop();
else break;
}
if(q.empty()){
continue;
}
int id=q.top().fi;
int w=q.top().se;
q.pop();
// cout<<i<<" "<<id<<" "<<w<<" "<<(id-i)*(id-i)<<"\n";
int num=min(c[i],-w);
ans+=num*(id-i)*(id-i);
if(c[i]+w<0){//abs(c[i])<abs(w)
q.push(mp(id,c[i]+w));
}
if(c[i]+w>0){//abs(c[i])>abs(w)
c[i]+=w;
i--;
}
}
cout<<ans<<" ";
ans=0;
// cout<<"\n";
for(int i=n+1;i>=1;i--){
if(d[i]<=0){
if(d[i]<0)q2.push(mp(i,d[i]));
continue;
}
if(q2.empty())continue;
int id=q2.top().fi;
int w=q2.top().se;
q2.pop();
int num=min(d[i],-w);
// cout<<num<<" "<<i<<" "<<id<<" "<<(id-i)*(id-i)<<"\n";
ans+=num*(id-i)*(id-i);
if(d[i]+w<0){
q2.push(mp(id,d[i]+w));
}
if(d[i]+w>0){
d[i]+=w;
i--;
}
}
cout<<ans;
return 0;
}
正确写法应该如下:按扫到的顺序入队。两遍的区别在于大小根队。负数做统计,正数推入列。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;i++)
int n;
const int N=1e6+10;
int a[N],cf[N];
#define pii pair<int,ll>
#define fi first
#define se second
#define mp make_pair
priority_queue<pii,vector<pii>,greater<pii>>q1;
priority_queue<pii>q2;
int main(){
// freopen("sequence4.in","r",stdin);
// freopen("sequence4.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
For(i,1,n)cin>>a[i];
For(i,1,n+1)cf[i]=a[i]-a[i-1];
ll ans1=0;
For(i,1,n+1){
if(cf[i]>0)q1.push(mp(i,cf[i]));
if(cf[i]>=0)continue;
ll x=-cf[i];
int r=i-1;
while(x>0){
pii t=q1.top();
int l=t.fi;
ll cnt=t.se;
q1.pop();
ll y=min(cnt,x);
ans1+=y*(r-l+1)*(r-l+1);
x-=y;cnt-=y;
if(cnt>0)q1.push(mp(l,cnt));
}
}
ll ans2=0;
For(i,1,n+1){
if(cf[i]>0)q2.push(mp(i,cf[i]));
if(cf[i]>=0)continue;
ll x=-cf[i];
int r=i-1;
while(x>0){
pii t=q2.top();
int l=t.fi;
ll cnt=t.se;
q2.pop();
ll y=min(cnt,x);
ans2+=y*(r-l+1)*(r-l+1);
x-=y;cnt-=y;
if(cnt>0)q2.push(mp(l,cnt));
}
}
cout<<ans1<<" "<<ans2;
return 0;
}
把操作序列看成一次复制+若干粘贴。粘贴次数构成序列a.Y个数可以由以下模拟得出:
第零次:剪贴板空,编辑器Y。
第一次:剪贴板Y,a1=2。文本编辑器:YYY(3)。
第二次:剪贴板:YYY,a2=3。编辑器YYYYYYYYYYYY(12)
也就是\(\prod(a_i+1)\),方便起见我们也可以令\(a'_i=a_i+1\),用新的\(a'_i\) 代替原序列。
问题转化:\(\prod a_i>n\),时间:\(nx+\sum(a_i-1)y=nx-ny+y\sum a_i=n(x-y)+y\sum a_i\)。最小化 \(\sum a_i\),且乘积固定,考虑平均分配。
实现的第一部分是二分找最大的 \(t\) 满足 \(t^k\le n\)。n++是为了满足>n的限制。然后令\(a_i=t\),把\(t^k\)乘出来,如果不够n就一直\(/t*(t+1)\),也就是基本不等式尽量平均分配,多出来的1平均撒到每个\(a_i\)头上,多撒的加进总和里。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define For(i,l,r) for(int i=l;i<=r;i++)
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define pb push_back
ll n,x,y,mn=1e18;
ll get(int k){
ll l=1,r=n,ans=1;
while(l<=r){
ll mid=l+(r-l)/2,t=1;bool f=0;
For(i,1,k){if(t>n/mid){f=1;break;}t=t*mid;}
if(f){r=mid-1;}else{ans=mid,l=mid+1;}
}
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>x>>y;n++;
For(k,1,60){
ll t=get(k),w=1,cnt=0;For(i,1,k)w=w*t;
while(w<n){w=w/t*(t+1);cnt++;}
mn=min(mn,k*(x-y)+(t*k+cnt)*y);
}
cout<<mn;
return 0;
}
染色操作会覆盖之前染的色,非常麻烦。我们可以考虑倒着染,这样新染上的不会盖掉之前染的。
观察发现染色有个特点,那就是会染连续的一段。于是考虑 \(f_{i,j,0/1}\) 表示用后面\(i\)种颜色染色,总染色长度j,目前在左/右端点。转移这样写:
\(f(i,j,0)\rightarrow f(k,x,0)\)(往左染色)
\(f(i,j,0)\rightarrow f(k,x,1)\)(往右边染色,因为是有效染色,所以超出去的颜色为k)
考虑什么时候的(k,x)转移合法。对于颜色k,k+1,...i-1我们要求染色时不超出颜色段以满足k是第一个超出去的,不能超画板边界,并且恰好走了对应的步数(例如第二种位移了x-1个格子)。
这个判定的做法要再考虑一个dp,g(x,y)表示当前用到染料x ,能否在不超过颜色段的情况下走到y。
具体实现上的细节:
左右转移是一个意思,可以去掉第三维改成*2
\(f(i,j)\)表示

浙公网安备 33010602011771号