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<