CF1420E

题面
题意:
有 nn 个守卫排成一行,编号为 11 到 nn。这些守卫里有的拿着盾牌,有的没有。一个守卫只能同时拿一个盾牌。
称一对守卫是被保护的,当且仅当这两个守卫都没拿盾牌,但他们之间有守卫拿了盾牌。
每一秒,指挥官可以下达两种命令中的一种,分别是:
1:选一个带盾牌的守卫,把它的盾牌给他左边的守卫
2:选一个带盾牌的守卫,把它的盾牌给他右边的守卫
请求出时刻 0 到 n*(n-1)/2中每个时刻最多有多少对守卫是被保护的。

解决:
我们不妨在每一个以每一个1为间断处,处理出一个长度为\(m\)的数组b[m],表示第i处连续的无盾的士兵有多少个,那么每次转移盾牌的操作即可视为对于某个\(i和i+1\),使\(b[i]和[i+1]\)分别+-1,每次操作后求的答案即是

\(\sum\limits_{i=1}^{m}\sum\limits_{j=i+1}^{m}b_i b_j =1/2(\sum\limits_{i=1}^{m}\sum\limits_{j=1}^{m}b_i b_j-\sum\limits_{i=1}^{m}b_i^2) 1/2((\sum\limits_{i=1}^{m}s_i)^2-\sum\limits_{i=1}^{m}s_i^2)\)
这个和式前面是定值,所以我们要最多的话即为后面的式子最小。

然后我就不会了,盗用网友的智慧,认识自己的蒟蒻

首先引入这样一个问题:

对于两个数组\(a_i和b_i\),如果\(\sum\limits_{i=1}^{m}a_i==\sum\limits_{i=1}^{m}b_i\),若通过对A的\(i和i+1\)分别加减一操作使得A数组转化到B数组,则在A,B前k-1位相等的情况下使得A,B第k位相等的所需要的操作数是\(\left |\sum\limits_{i=1}^{k}(a_i-b_i)\right |\)等价于\(\left|\sum\limits_{i=1}^{k}a_i-\sum\limits_{i=1}^{k}b_i\right|\),令其为\(f_{A,B}(i)\).

这个的证明可以大致理解为假设前\(i\)位满足条件时,第\(i+1\)位必定时迎合了前面的改变故前面所变化的值一定都会在第\(i+1\)位体现的,故当前k位均满足时需要的操作数为\(\sum\limits_{i=1}^{k}f_{A,B}(i)\)

然后将这个问题与本题结合,首先设\(c[i]位b[i]的\)\(dp\)数组\(dp[i][j][k]\)表示在前\(i\)个位置,当前可用总的0的数值为\(j\),当前操作数位\(k\),则在向第\(i+1\)位转移时,我们先枚举此后可用的0的总数\(l(l>=j)\)此时在第i+1位放置的0的个数即为\(l-j\),而操作次数呢,根据上面引入的问题,我们可以发现操作次数的增加为\(\left|l-c[i+1]\right|\).据此我们可以写出转移方程\(dp[i+1][l][k+abs(l-c[i+1])]=dp[i][j][k]+(l-j)^2\),同时在对自己取最小值,看上去复杂度是\(O(n^5)\),但由于很多状态不合法,所以复杂度没这么高。

其实有严格的\(O(n^4logn)\)的斜率优化做法但我不会

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<bitset>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rpe(i,a,b) for(int i=a;i>=b;--i)
#define pts putchar('\n')
#define ptc putchar(' ')
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int maxn=82;
const int mod=998244353;
const int base=131;
const double eps=1e-7;
 
namespace IO{
    ll read(){	
		ll a=1,b=0;char c=getchar();
		while(c>'9'||c<'0'){if(c=='-')a=-1;c=getchar();} 
		while(c>='0'&&c<='9'){b=(b<<3)+(b<<1)+c-'0';c=getchar();}
		return a*b ;
	}
	void print (ll x){
		if(x<0) putchar('-'),x=-x;
		if(x>9) print(x/10);
		putchar(x%10+'0');
	} 
}
using namespace IO;
inline void DEBUG(ll x){cout<<"asdasd"<<x<<endl;}
 
int n;
int b[maxn],m=1,c[maxn];
int dp[maxn][maxn][maxn*maxn];
 
int main(){
#ifndef ONLINE_JUDGE
    freopen("in0.txt","r",stdin);
#endif
	n=read();
	int tmp;bool flag=0;
	rep(i,1,n) {
		tmp=read();
		if(tmp==1) m++;
		else b[m]++;
	}
	rep(i,1,m) c[i]=c[i-1]+b[i];
	memset(dp,0x3f,sizeof(dp));
	dp[0][0][0]=0;
	int num=n*(n-1)/2;
	rep(i,0,m-1){//枚举当前位置
		rep(j,0,c[m]){//当前可用总数量
			rep(opt,0,num){
				if(dp[i][j][opt]==0x3f3f3f3f) continue;
				rep(l,j,c[m]){
					if(opt+abs(l-c[i+1])>num) continue;
//如果此时的操作数大于最大操作数
					int &tmp=dp[i+1][l][opt+abs(l-c[i+1])];
					tmp=min(tmp,dp[i][j][opt]+(l-j)*(l-j));
				}
			}
		}
	}
	int mn=0x3f3f3f3f;
	rep(i,0,num){
		mn=min(mn,dp[m][c[m]][i]);
		print((c[m]*c[m]-mn)>>1);pts;
	}
    return 0;	
}
 

posted @ 2020-11-21 00:36  Mr_cold  阅读(179)  评论(0)    收藏  举报