4.11省选模拟

\(T1\)

想到了优化前的\(dp\)

\(40pts\)无优化

#include<bits/stdc++.h>
#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<i;j++)
        {
            int now;
            if(a[i]>=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<<Ans<<"\n";
}

把转移写出来

重新考虑一下我们这一维的含义,其实可以看成是第二维表示成数字大小

那么

\(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]<j,dp[i+1][j]=dp[i+1][j]+j\)区间加等差数列

\(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<bits/stdc++.h>
#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<int> 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<sy[r];++i) res=min(res,mx[i]+lazy[i]);
	return res;
}
void add(int l,int r,int v)
{
	if(sy[l]==sy[r])
	{
		for(int i=l;i<=r;++i) val[i]+=v;
		remake(sy[l]);
		return;
	}
	for(int i=l;i<=R[sy[l]];++i) val[i]+=v;
	for(int i=r;i>=L[sy[r]];--i) val[i]+=v;
	remake(sy[l]);remake(sy[r]);
	for(int i=sy[l]+1;i<sy[r];++i) lazy[i]+=v;
	return ;
}
void addc(int l,int r)
{
	if(sy[l]==sy[r])
	{
		for(int i=l;i<=r;++i) val[i]+=i;
		remake(sy[l]);
		return;
	}
	for(int i=l;i<=R[sy[l]];++i) val[i]+=i;
	for(int i=r;i>=L[sy[r]];--i) val[i]+=i;
	remake(sy[l]);remake(sy[r]);
	for(int i=sy[l]+1;i<sy[r];++i) ch[i]++;
	return ;
}
void re()
{
	for(int i=1;i<=sy[n];++i) push_down(i);
	for(int i=1;i<=sy[n];++i) remake(i);
}
void out()
{
	re();
//	cout<<sy[i]<<" "<<L[sy[i]]<<" "<<R[sy[i]]<<endl;
	for(int i=0;i<=n;++i) cout<<val[i]<<" ";
	cout<<endl;
}
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'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;i<n;++i)
	{
		if(a[i+1]>s[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<<res;
}

\(T2\)

正解竟然是搜索....貌似是基于\(border\)集合大小有限得出的

暴力\(dfs\)

#include<bits/stdc++.h>
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<<Fin<<"\n";
}

正解需要\(Border\)理论,不会

\(T3\)

重新透彻了一下模拟费用流,考场上一直误以为是\(dp,\)导致根本没想到

其实\(dp\)的过程就是找匹配,思想很接近模拟费用流了,但是没有转化到匹配上

粘一下暴力\(dp\)

#include<bits/stdc++.h>
#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<int,vector<int>,greater<int> >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<<Ans;
//       return 0;
//    }
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++)//枚举目前选到哪个位置了
    {
    	memcpy(Mid,dp,sizeof(Mid));
//    	memset(dp,0x3f,sizeof(dp));
        for(int j=0;j<=min(i-1,k-1);j++)//枚举我们目前选了几个人了
        {
//            for(int z=j;z<i;z++)//枚举上面一层上一个选的是哪个
//            {
            	for(int v=j+1;v<=i;v++)
            	{
            		dp[j+1][v]=min(dp[j+1][v],min(Mid[j+1][v],Mid[j][v-1]+a[v]+b[i]));
            		dp[j+1][v]=min(dp[j+1][v],dp[j+1][v-1]);
					if(j+1==k)
	                {
	                   Ans=min(Ans,dp[j+1][v]);
	                }
            		continue;
            	}
//                dp[j+1][Min[z+1][i]]=min(dp[j+1][Min[z+1][i]],dp[j][z]+a[Min[z+1][i]]+b[i]);
//                //区间是z+1,i
                
//            }
        }
    }
    cout<<Ans<<"\n";
}

正解

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define INF 10000000000
#define int long long
#define MAXN 500005
using namespace std;
int a[MAXN],b[MAXN],Sum,num,n,k;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >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<<now<<" "<<id<<"\n";
     	 if(b[i]+now+mid>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<<Sum-k*Ans<<"\n";
}
posted @ 2022-04-11 16:46  Authentic_k  阅读(31)  评论(0编辑  收藏  举报