【Codeforces】Educational Codeforces Round 59/Round1107 D-G题解

传送门:CF1107
有一种CF越来越板/水的感觉,完全是手速场(不看题解就水完了啊


D.Compression

前缀和后 c h e c k check check的复杂度可以做到 O ( ( n x ) 2 ) O((\dfrac{n}{x})^2) O((xn)2)

称矩阵是 k k k可行的当且仅当 x = k x=k x=k时满足题中条件。

若矩阵同时是 a , b a,b a,b可行的,且 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,则矩阵也是 a ⋅ b a·b ab可行的。

分解 n n n的质因子后枚举每个质因子的幂次即可。

复杂度玄学能过。

#include
typedef long long ll;
using namespace std;
const int N=5205;

int n,s[N][N],p[N],num,ans;

char cp;
inline int rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
}

inline void ins(int x,int y)
{
    int i,msk;
	for(cp=getchar();;cp=getchar()){
		if(cp>='0' && cp<='9'){
			msk=cp-'0';break;
		}
		if(cp>='A' && cp<='F'){
			msk=10+cp-'A';
			break;
		}
	}
	for(i=0;i<4;++i) s[x][y+i]=(msk>>(3-i)&1);
}

inline bool ck(int x)
{
	int i,j,k,bs=x*x;
	for(i=x;i<=n;i+=x)
	 for(j=x;j<=n;j+=x){
	 	k=s[i][j]-s[i-x][j]-s[i][j-x]+s[i-x][j-x];
	 	if((k!=bs)&&(k!=0)) return false;
	 }
	return true;
}

int main(){
	int i,j,k;
    scanf("%d",&n);
    for(i=1;i<=n;++i)
    	for(j=1;j<=n;j+=4) ins(i,j);
    for(i=1;i<=n;++i)
     for(j=1;j<=n;++j)
      s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    k=n;
	for(i=2;i*i<=k;++i) if(!(k%i)){
		p[++num]=i;for(;!(k%i);k/=i);
	}
	if(k>1) p[++num]=k;ans=1;
	for(i=1;i<=num;++i){
		for(k=p[i],j=k;!(n%j);j*=k){
			if(!ck(j)) break;
		}
		ans*=(j/k);
	}
	printf("%d",ans);
	return 0;
} 

E.Vasya and Binary String

先完全背包求出 C i C_i Ci表示删除连续一段 0 / 1 0/1 0/1的最大花费。

d p [ l ] [ r ] [ 0 / 1 ] [ l e n ] dp[l][r][0/1][len] dp[l][r][0/1][len]表示将区间 [ l , r ] [l,r] [l,r]删除到只剩下 l e n len len 0 / 1 0/1 0/1的最大代价, g [ l ] [ r ] g[l][r] g[l][r]表示将区间 [ l , r ] [l,r] [l,r]全部删除的最大代价。

d p [ l ] [ r ] [ S r ] [ l e n ] = m a x ( d p [ l ] [ k ] [ S r ] [ l e n − 1 ] + g [ k + 1 ] [ r − 1 ] ) dp[l][r][S_r][len]=max(dp[l][k][S_r][len-1]+g[k+1][r-1]) dp[l][r][Sr][len]=max(dp[l][k][Sr][len1]+g[k+1][r1])
g [ l ] [ r ] = m a x ( d p [ l ] [ k ] [ 0 / 1 ] [ l e n ] + C l e n + g [ k + 1 ] [ r ] ) g[l][r]=max(dp[l][k][0/1][len]+C_{len}+g[k+1][r]) g[l][r]=max(dp[l][k][0/1][len]+Clen+g[k+1][r])

复杂度 O ( n 4 ) O(n^4) O(n4)

#include
typedef long long ll;
using namespace std;
const int N=105;

int n,a[N];
char s[N];
ll dp[N][N][2][N],g[N][N],nf,cn[2],c[N];

inline void upp(ll &x,ll y){if(y>x) x=y;}

int main(){
	int i,j,k,l,r;
	memset(dp,-0x3f,sizeof(dp));
	memset(g,-0x3f,sizeof(g));nf=g[0][0];
    scanf("%d%s",&n,s+1);
    for(i=1;i<=n;++i) scanf("%d",&a[i]),s[i]-='0';
	for(i=1;i<=n;++i)
	 for(j=n;j>=i;--j) 
	 	c[j]=max(c[j],c[j-i]+a[i]);
	for(i=1;i<=n;++i) dp[i][i][s[i]][1]=0,g[i+1][i]=0;g[1][0]=0;
	
	for(l=n;l;--l)
	 for(r=l;r<=n;++r)
	   for(i=1;l+i-1<=r;++i){
	   	  for(k=0;k<2;++k) cn[k]=dp[l][r][k][i];
	 	  for(k=r+1;k<=n;++k) if(cn[s[k]]>nf)
		   upp(dp[l][k][s[k]][i+1],cn[s[k]]+g[r+1][k-1]);
		  for(k=r;k<=n;++k)
		    upp(g[l][k],max(cn[0],cn[1])+c[i]+g[r+1][k]);
	   }
	printf("%I64d",g[1][n]);
	return 0;
} 

F.Vasya and Endless Credits

构造二分图,左部 n n n个点分别表示 n n n个贷,右部 n n n个点中第 i i i个点表示倒数第 i i i个月借贷。 g ( i , j ) = m a x ( 0 , a i − m i n ( j − 1 , k i ) × b i ) g(i,j)=max(0,a_i-min(j-1,k_i)\times b_i) g(i,j)=max(0,aimin(j1,ki)×bi)

那么就是裸的二分图最大权匹配。费用流会T,跑个KM即可。

#include
typedef long long ll;
using namespace std;
const int N=505;
const ll inf=1e18;

int n,tim,vs[2][N],bel[N];
ll g[N][N],tg[2][N],slack[N],ans;

inline void upp(ll &x,ll y){if(y>x) x=y;}
inline void dnn(ll &x,ll y){if(y<x) x=y;}

bool dfs(int x)
{
	int i;ll v;vs[0][x]=tim;
	for(i=1;i<=n;++i) if(vs[1][i]!=tim){
		v=tg[0][x]+tg[1][i]-g[x][i];
		if(v==0){
			vs[1][i]=tim;
			if((!bel[i])||dfs(bel[i])){
			    bel[i]=x;return true; 
			} 
		}else dnn(slack[i],v);
	}
	return false;
} 

int main(){
	int i,j,x,y,z;ll d;
	scanf("%d",&n);
	for(i=1;i<=n;++i){
		scanf("%d%d%d",&x,&y,&z);
		for(j=1;j<=n;++j) g[i][j]=max(0LL,x-(ll)y*min(j-1,z));
	}
	for(i=1;i<=n;++i){
	 for(d=0LL,j=1;j<=n;++j)
	  upp(d,g[i][j]);
	 tg[0][i]=d;	
	}
	for(x=1;x<=n;++x){
		memset(slack,0x7f,sizeof(slack));
		for(;;){
			tim++;if(dfs(x)) break;d=inf;
			for(i=1;i<=n;++i) if(vs[1][i]!=tim) dnn(d,slack[i]);
			for(i=1;i<=n;++i) if(vs[0][i]==tim) tg[0][i]-=d;
			for(i=1;i<=n;++i) vs[1][i]==tim?tg[1][i]+=d:slack[i]-=d;
		}
	}
	for(i=1;i<=n;++i) ans+=g[bel[i]][i];
    printf("%I64d",ans);
	return 0;
}

G - Vasya and Maximum Profit

e i = d i − d i − 1 e_i=d_i-d_{i-1} ei=didi1。单调栈对于求出 [ l i , r i ] [l_i,r_i] [li,ri]表示 m a x j = l i r i e j = e i max_{j=l_i}^{r_i} e_j=e_i maxj=liriej=ei的最大区间。

再线段树维护一下区间前缀和后缀和最大值即可。

#include
typedef long long ll;
using namespace std;
const int N=3e5+10;

int n,a,lf[N],rt[N];
ll v[N],prf[N],suf[N],ans;
int c[N],d[N],stk[N],top;

char cp;
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
}

#define lc k<<1
#define rc k<<1|1
#define mid (l+r>>1)
#define pu(k) mx[k]=max(mx[lc],mx[rc])
struct seg{
	ll mx[N<<2];
	
	void build(int k,int l,int r)
	{
		if(l==r) {mx[k]=v[l];return;}
		build(lc,l,mid);build(rc,mid+1,r);
		pu(k);
	}
	
	ll ask(int k,int l,int r,int L,int R)
	{
		if(L<=l && r<=R) return mx[k];
		if(R<=mid) return ask(lc,l,mid,L,R);
		if(L>mid) return ask(rc,mid+1,r,L,R);
		return max(ask(lc,l,mid,L,R),ask(rc,mid+1,r,L,R));
	}
	
}qz,hz;

int main(){
	int i,j,k,pre=0;ll res;
	rd(n);rd(a);v[0]=v[n+1]=0LL; 
	for(i=1;i<=n;++i){
		rd(d[i]);rd(c[i]);k=d[i];d[i]-=pre;pre=k;
		c[i]=a-c[i];v[i]=v[i-1]+c[i];ans=max(ans,(ll)c[i]);
	}
	qz.build(1,1,n);memcpy(prf,v,sizeof(prf));	
	for(i=n;i;--i) v[i]=v[i+1]+c[i];
	hz.build(1,1,n);memcpy(suf,v,sizeof(suf));
	stk[0]=1;
	for(i=2;i<=n;++i){
		for(;top && d[stk[top]]<=d[i];--top)
		  rt[stk[top]]=i-1;
		lf[i]=stk[top];stk[++top]=i;
	}
	for(;top;--top) rt[stk[top]]=n;
	for(i=2;i<=n;++i){
		res=max(0LL,qz.ask(1,1,n,i,rt[i])-prf[i-1]);
		res+=hz.ask(1,1,n,lf[i],i-1)-suf[i];
		ans=max(ans,res-(ll)d[i]*d[i]);
	}
	printf("%I64d",ans);
	return 0;
}

你可能感兴趣的:(区间DP,CF)