4.11省选模拟
\(T1\)
想到了优化前的\(dp\)
\(40pts\)无优化
#include
#define INF 2147483647
#define MAXN 1005
using namespace std;
int n,a[MAXN],dp[MAXN],Max[MAXN],Mid[MAXN];
int main()
{
freopen("separate.in","r",stdin);
freopen("separate.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
Max[i]=max(Max[i-1],a[i]);
}
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
memcpy(Mid,dp,sizeof(dp));
memset(dp,0x3f,sizeof(dp));
for(int j=0;j=a[j]) now=i;
else now=j;
dp[now]=min(dp[now],Mid[j]+a[now]);
dp[j]=min(dp[j],Mid[j]+Max[i]);
//其实转移就有两种
//第二个整体加不管
//第一个也分为两种情况,如果a[j]>a[i]那么所有大于a[i]的位置区间加a[j]
//貌似是可以用线段树维护的,但是太麻烦,或许也能cdq或者决策单调性?
//弃了,40跑路.
}
}
int Ans=INF;
for(int i=0;i<=n;i++)
{
Ans=min(Ans,dp[i]);
}
cout<
把转移写出来
重新考虑一下我们这一维的含义,其实可以看成是第二维表示成数字大小
那么
\(dp[i][j]\)表示前\(i\)个点,一个序列末端是\(j,\)另一个序列末端是\(s[i]\)的最小士气值
转移易得
\(a[i+1]>s[i],dp[i+1][j]=dp[i][j]+a[i+1]\)
\(a[i+1]<=s[i]\)
\(Sit_1:\)
\(a[i+1]
\(Sit_2\)
\(a[i+1]>=j,dp[i+1][a[i+1]]=dp[i][j]+a[i+1]\)找一个区间最小值加进去
\(dp[i+1][j]=dp[i][j]+s[i]\)区间加
由于区间加等差数列可以看成斜率,找一个凸包最下面的点就好了,显然
直接大力分块,维护凸包即可
#define Eternal_Battle ZXK
#include
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define MAXN 70001
#define MAXM 1001
using namespace std;
int blo,sy[MAXN],L[MAXM],R[MAXM],nowp[MAXM];
int n,a[MAXN],s[MAXN];
ll lazy[MAXM],mx[MAXM],val[MAXN],ch[MAXM];
vector vec[MAXM];
inline double slope(int x,int y) {return 1.0*(val[x]-val[y])/(x-y);}
void push_down(int x)
{
for(int i=L[x];i<=R[x];++i) val[i]+=1ll*i*ch[x]+lazy[x];
ch[x]=lazy[x]=0;
}
void remake(int x)
{
push_down(x);
vec[x].clear();
for(int i=L[x];i<=R[x];++i)
{
if(vec[x].size()&&val[i]>=val[vec[x][vec[x].size()-1]]) continue;
while(vec[x].size()>1&&slope(i,vec[x][vec[x].size()-1])<=slope(vec[x][vec[x].size()-1],vec[x][vec[x].size()-2])) vec[x].erase(--vec[x].end());
vec[x].push_back(i);
}
nowp[x]=vec[x].size()-1;
mx[x]=val[vec[x][nowp[x]]];
}
void repair(int x)
{
while(nowp[x]!=0&&val[vec[x][nowp[x]]]+ch[x]*vec[x][nowp[x]]>=val[vec[x][nowp[x]-1]]+ch[x]*vec[x][nowp[x]-1]) --nowp[x];
mx[x]=val[vec[x][nowp[x]]]+ch[x]*vec[x][nowp[x]];
}
ll query(int l,int r)
{
ll res=INF;
if(sy[l]==sy[r])
{
repair(sy[l]);
for(int i=l;i<=r;++i) res=min(res,i*ch[sy[i]]+val[i]+lazy[sy[i]]);
return res;
}
for(int i=sy[l];i<=sy[r];++i) repair(i);
for(int i=l;i<=R[sy[l]];++i) res=min(res,i*ch[sy[i]]+val[i]+lazy[sy[i]]);
for(int i=r;i>=L[sy[r]];--i) res=min(res,i*ch[sy[i]]+val[i]+lazy[sy[i]]);
for(int i=sy[l]+1;i=L[sy[r]];--i) val[i]+=v;
remake(sy[l]);remake(sy[r]);
for(int i=sy[l]+1;i=L[sy[r]];--i) val[i]+=i;
remake(sy[l]);remake(sy[r]);
for(int i=sy[l]+1;i'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
signed main()
{
freopen("separate.in","r",stdin);
freopen("separate.out","w",stdout);
memset(val,0x3f,sizeof(val));
val[0]=0;
n=read();
for(int i=1;i<=n;++i) a[i]=read();
blo=sqrt(n);
for(int i=0;i<=n;++i)
{
sy[i]=(i/blo)+1;
if(!L[sy[i]]) L[sy[i]]=i;
R[sy[i]]=i;
}
L[1]=0;
for(int i=1;i<=sy[n];++i) remake(i);
for(int i=1;i<=n;++i) s[i]=max(s[i-1],a[i]);
for(int i=0;is[i])
{
add(0,a[i+1],a[i+1]);
}
else
{
if(a[i+1]+1<=s[i]) addc(a[i+1]+1,s[i]);
ll p=query(0,a[i+1])+a[i+1];
if(0<=a[i+1]-1) add(0,a[i+1]-1,s[i]);
push_down(sy[a[i+1]]);
val[a[i+1]]=p;
remake(sy[a[i+1]]);
}
}
for(int i=1;i<=sy[n];++i) remake(i);
ll res=INF;
for(int i=0;i<=n;++i) res=min(res,val[i]);
cout<
\(T2\)
正解竟然是搜索....貌似是基于\(border\)集合大小有限得出的
暴力\(dfs\)
#include
using namespace std;
int n,k,Fin,Ans[1000];
void dfs(int now)
{
if(now==n+1)
{
int res=0;
for(int j=2;j<=n;j++)
{
int len=0;
while(Ans[1+len]==Ans[j+len]) len++;
res=max(res,len);
}
Fin+=res;
return ;
}
for(int i=1;i<=k;i++)
{
Ans[now]=i;
dfs(now+1);
}
}
int main()
{
freopen("lorem.in","r",stdin);
freopen("lorem.out","w",stdout);
scanf("%d%d",&n,&k);
dfs(1);
cout<
正解需要\(Border\)理论,不会
\(T3\)
重新透彻了一下模拟费用流,考场上一直误以为是\(dp,\)导致根本没想到
其实\(dp\)的过程就是找匹配,思想很接近模拟费用流了,但是没有转化到匹配上
粘一下暴力\(dp\)
#include
#define int long long
#define MAXN 1005
#define INF LLONG_MAX
using namespace std;
int a[MAXN],b[MAXN],Ans=INF,n,k;
int dp[MAXN][MAXN],Min[MAXN][MAXN],Mid[MAXN][MAXN];
signed main()
{
freopen("world.in","r",stdin);
freopen("world.out","w",stdout);
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&b[i]);
}
// if(n>1000)
// {
// priority_queue,greater >q;
// for(int i=1;i<=n;i++)
// {
// q.push(a[i]+b[i]);
// }
// int cnt=k,Ans=0;
// while(cnt--)
// {
// Ans+=q.top();
// q.pop();
// }
// cout<
正解
#define Eternal_Battle ZXK
#include
#define INF 10000000000
#define int long long
#define MAXN 500005
using namespace std;
int a[MAXN],b[MAXN],Sum,num,n,k;
priority_queue,vector >,greater > >q;
bool check(int mid)
{
while(q.size())q.pop();
int res=0;
num=0;
Sum=0;
for(int i=1;i<=n;i++)
{
q.push(make_pair(a[i],0));
int now=q.top().first;
int id=q.top().second;
// cout<0)
{
continue;
}
Sum+=b[i]+now+mid;
if(id==0) num++;
q.pop();
q.push(make_pair(-b[i]-mid,1));
}
return num>=k;
}
signed main()
{
freopen("world.in","r",stdin);
freopen("world.out","w",stdout);
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
int l=-INF,r=INF,mid,Ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
//如果大于等于返回true
if(check(mid))
{
l=mid+1;
Ans=mid;
}
else
{
r=mid-1;
}
}
check(Ans);
cout<