JZOJ 3918 蛋糕
题目
把一个矩阵横切三刀,竖切三刀,问当中的子矩阵总和最小的最大能是多少
分析
首先相当暴力的方法就是暴力切的位置然后用前缀和,时间复杂度应该是 O ( n 6 ) O(n^6) O(n6),但是这个东西是二分的套路,考虑二分答案,首先竖切三刀,然后 O ( n ) O(n) O(n)判断,只要能分成4段或以上即为合法
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,m,ans,a[101][101],tot;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline bool check(int j1,int j2,int j3,int k){
rr int s1=0,s2=0,s3=0,s4=0,kuai=0;
for (rr int i=1;i<=n;++i){
s1+=a[i][j1],s2+=a[i][j2]-a[i][j1],s3+=a[i][j3]-a[i][j2],s4+=a[i][m]-a[i][j3];
if (s1>=k&&s2>=k&&s3>=k&&s4>=k) s1=s2=s3=s4=0,++kuai;
}
return kuai>=4;
}
signed main(){
n=iut(); m=iut();
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=m;++j){
rr char c=getchar();
while (!isdigit(c)) c=getchar();
a[i][j]=a[i][j-1]+(c^48);
}
for (rr int i=1;i<=n;++i) tot+=a[i][m];
for (rr int j1=1;j1<m-2;++j1)
for (rr int j2=j1+1;j2<m-1;++j2)
for (rr int j3=j2+1;j3<m;++j3){
rr int l=1,r=tot>>4;
while (l<r){
rr int mid=(l+r+1)>>1;
if (check(j1,j2,j3,mid)) l=mid;
else r=mid-1;
}
ans=ans>l?ans:l;
}
return !printf("%d",ans);
}
BZOJ 3743 JZOJ 3919 志愿者
题目
一颗树
n
n
n个点,
n
−
1
n-1
n−1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有
K
K
K个人(分布在
K
K
K个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这
K
K
K个人分别送回去。
请你回答,对于
i
=
1
∼
n
i=1\sim n
i=1∼n,如果在第i个点举行聚会,司机最少需要多少时间把
K
K
K个人都送回家。
分析
那我们会发现,司机花费的时间其实就是 2 * (【第i个点 + K个点组成的点集】生成的最小的树的边权和) - K个点中离第i个点最远的距离 即可。
那么分别处理,换根O(n)解决
代码
#include <cstdio>
#include <cctype>
#define rr register
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=500011;
struct node{int y,w,next;}e[N<<1];
typedef long long lll; lll dis[N],dp[N][3];
int m,s[N],n,ls[N],is[N],k=1;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(lll ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void dfs1(int x,int fa){
s[x]=is[x],dis[x]=0;
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fa){
dfs1(e[i].y,x),s[x]+=s[e[i].y];
dis[x]+=dis[e[i].y]+(s[e[i].y]?e[i].w:0);
}
}
inline void dfs2(int x,int fa){
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fa){
dis[e[i].y]=dis[x];
if (s[e[i].y]) dis[e[i].y]-=e[i].w;
if (s[e[i].y]<m) dis[e[i].y]+=e[i].w;
dfs2(e[i].y,x);
}
}
inline void dfs3(int x,int fa){
dp[x][0]=dp[x][1]=(is[x]?0:-1e18);
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fa){
dfs3(e[i].y,x);
if (dp[e[i].y][0]+e[i].w>dp[x][0])
dp[x][1]=dp[x][0],dp[x][0]=dp[e[i].y][0]+e[i].w;
else if (dp[e[i].y][0]+e[i].w>dp[x][1]) dp[x][1]=dp[e[i].y][0]+e[i].w;
}
}
inline void dfs4(int x,int fa){
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fa){
if (dp[e[i].y][0]+e[i].w==dp[x][0]) dp[e[i].y][2]=dp[x][1]+e[i].w;
else dp[e[i].y][2]=dp[x][0]+e[i].w;
dp[e[i].y][2]=max(dp[e[i].y][2],dp[x][2]+e[i].w);
dfs4(e[i].y,x);
}
}
signed main(){
n=iut(),m=iut();
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut(),w=iut();
e[++k]=(node){y,w,ls[x]},ls[x]=k,
e[++k]=(node){x,w,ls[y]},ls[y]=k;
}
for (rr int i=1;i<=m;++i) is[iut()]=1;
dfs1(1,0),dfs2(1,0),dfs3(1,0),
dp[1][2]=is[1]?0:-1e18,dfs4(1,0);
for (rr int i=1;i<=n;++i,putchar(10))
print((dis[i]<<1)-max(dp[i][0],dp[i][2]));
return 0;
}
JZOJ 3920 噪音
题目
有 M M M个牛棚,编号 1 ∼ M 1\sim M 1∼M,刚开始所有牛棚都是空的。有 N N N头牛,编号 1 ∼ N 1\sim N 1∼N,这 N N N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第 i i i头奶牛选择走进第 p [ i ] p[i] p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛 x x x进入牛棚 y y y之后,牛棚y里的所有奶牛们都会喊一声“欢迎欢迎,热烈欢迎”,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。最多可以使用 K K K次“清空”操作,每次“清空”操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。“清空”操作只能在噪音产生后执行。应该如何执行“清空”操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?
分析
首先这 n n n头牛没有什么用,真正有用的是总牛棚里牛的个数,设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个牛棚消除 j j j次所产生的最小噪音,那么 d p [ i ] [ j ] = d p [ i − 1 ] [ j − k ] + c a l c ( p [ i ] , k ) dp[i][j]=dp[i-1][j-k]+calc(p[i],k) dp[i][j]=dp[i−1][j−k]+calc(p[i],k)
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
typedef long long ll;
int m,n,a[101]; ll ans,dp[501];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline ll min(ll a,ll b){return a<b?a:b;}
inline ll calc(int w,int mo){
rr ll di=w/mo,yu=w%mo;
return (di*mo*(di+1)>>1)+yu*(di+1);
}
signed main(){
rr int nn=iut(); n=iut(); m=iut();
while (nn--) ++a[iut()];
memset(dp,0x7f,sizeof(dp)); dp[0]=0;
for (rr int i=1;i<=n;++i)
for (rr int j=m;~j;--j){
dp[j]+=calc(a[i],1);
for (rr int k=j-1;~k;--k)
dp[j]=min(dp[j],dp[k]+calc(a[i],j-k+1));
}
return !printf("%lld",dp[m]);
}