bzoj usaco 金组水题题解(2.5)

时间:2023-12-31 19:43:44

bzoj 2197: [Usaco2011 Mar]Tree Decoration

  树形dp。。f[i]表示处理完以i为根的子树的最小时间。

  因为一个点上可以挂无数个,所以在点i上挂东西的单位花费就是i所在子树里的最小单位花费。。

  所以每次求f[i]只要使子树里的数量都满足要求就好了。。i的祖先还要更多的话随时可以选某个节点多挂一些。。

  f[i]=sum{f[j]}+mincost[i]*max(need[i]-sum{need[j]},0)。。(j是i的儿子,mincost[i]表示子树i里的最小花费,need[i]表示i这颗子树需要的最少数量)

 #include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=;
struct zs{
int too,pre;
}e[maxn];
int last[maxn],mn[maxn],tot;
int i,j,k,n,m,a;
ll f[maxn],need[maxn];
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
void dfs(int x){
int i,to;ll sum=;
for(i=last[x],to=e[i].too;i;mn[x]=mn[x]>mn[to]?mn[to]:mn[x],sum+=need[to],f[x]+=f[to],i=e[i].pre,to=e[i].too)dfs(to);
if(sum>=need[x])need[x]=sum;else f[x]+=(need[x]-sum)*(ll)mn[x];
}
int main(){
n=read();for(rx=getchar();rx!='-';rx=getchar());rx=getchar();
need[]=read();mn[]=read();
for(i=;i<=n;i++)a=read(),e[i].pre=last[a],last[a]=e[i].too=i,need[i]=read(),mn[i]=read();
dfs();
printf("%lld\n",f[]);
return ;
}

bzoj 2097: [Usaco2010 Dec]Exercise 奶牛健美操

  没思路跑去看题解系列。。。二分答案。。

  假设二分出来的答案是mid,那么对于每颗子树,设根节点为i,那么删边后子树的直径不能超过mid。

  子树内的路径分两种情况,一种是经过i的,另一种是不经过i的。不经过i的显然可以递归i的子节点去删边,删完边后,如果i的任意两个儿子可以通过i凑成一条超过mid的路径的话,就贪心地把i和对长度贡献较大的孩子之间的连边删掉。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=;
struct child{
int x,len;
};
struct zs{
int too,pre;
}e1[maxn<<],e[maxn];
int tot,t1;
int l1[maxn],last[maxn],mxdep[maxn];
int i,j,mx,n,m,cnum,a,b,l,r,mid;
child ch[maxn];
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
inline void ins1(int a,int b){
e1[++t1].too=b;e1[t1].pre=l1[a];l1[a]=t1;
e1[++t1].too=a;e1[t1].pre=l1[b];l1[b]=t1;
}
inline void insert(int a,int b){e[++tot].too=b;e[tot].pre=last[a];last[a]=tot;}
void dfs1(int x,int fa){for(int i=l1[x];i;i=e1[i].pre)if(e1[i].too!=fa)insert(x,e1[i].too),dfs1(e1[i].too,x);}
bool cmp(child a,child b){return a.len<b.len;}
int dfs(int x,int val){
int i,ans=,to;
for(i=last[x];i&&ans<=mx;i=e[i].pre)
ans+=dfs(e[i].too,val);
if(ans>mx)return mx+;
cnum=;
for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too)
ch[++cnum].x=to,ch[cnum].len=mxdep[to]+;
sort(ch+,ch++cnum,cmp);ch[].len=;
for(i=cnum;i&&ch[i].len+ch[i-].len>val;i--)ans++;
mxdep[x]=ch[i].len;
return ans;
}
int main(){
srand();
n=read();mx=read();if(mx==n-){puts("");return ;}if(mx==n-){puts("");return ;}
for(i=;i<n;i++)a=read(),b=read(),ins1(a,b);
int rt=rand()%n+;
dfs1(rt,);
l=;r=n-;
while(l<r){
mid=(l+r)>>;
if(dfs(rt,mid)<=mx)r=mid;else l=mid+;
}
printf("%d\n",l);
return ;
}

bzoj 1694: [Usaco2007 Demo]Grazing on the Run

  又是英文题面+双倍经验= =同bzoj1742。。。。双倍经验==>双倍#1。。233

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=;
int f[maxn][];
int len,s,mxj,n;
int a[maxn],b[maxn];
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
int i,j,mxj,tmp;
n=read();s=read();
for(i=;i<=n;i++)a[i]=read();sort(a+,a++n);
for(i=;i<=n;b[i]=a[i+]-a[i],i++)f[i][]=f[i][]=abs(s-a[i])*n;
for(i=,mxj=n-;i<n;mxj--,i++)for(j=;j<=mxj;j++)
tmp=f[j][],f[j][]=min(f[j+][]+mxj*b[j],f[j+][]+mxj*(a[j+i]-a[j])),
f[j][]=min(tmp+mxj*(a[j+i]-a[j]),f[j][]+mxj*b[j+i-]);
printf("%d\n",min(f[][],f[][]));
return ;
}

bzoj 2501: [usaco2010 Oct]Soda Machine

  区间加后查询最大值。。写了离散化后二分。。其实离散化姿势正确的话是不用二分找出区间左右端点位置的TAT。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
int mp[maxn<<],l[maxn],r[maxn],sum[maxn<<];
int i,j,ans,n,size,nowsum;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
inline int get(int x){
int l=,r=size,mid;
while(l<r){
mid=(l+r+)>>;
if(mp[mid]<=x)l=mid;else r=mid-;
}return l;
}
int main(){
n=read();
for(i=;i<=n;i++)l[i]=mp[i]=read(),r[i]=mp[i+n]=read();
sort(mp+,mp++n*);size=;n<<=;
for(i=;i<=n;i++)if(mp[i]!=mp[i-])mp[++size]=mp[i];n>>=;
for(i=;i<=n;i++)sum[get(l[i])]++,sum[get(r[i])+]--;
for(i=;i<=size;i++){
nowsum+=sum[i];
if(nowsum>ans)ans=nowsum;
}
printf("%d\n",ans);
return ;
}

bzoj 1915: [Usaco2010 Open]奶牛的跳格子游戏

  dp+单调队列优化 http://www.cnblogs.com/czllgzmzl/p/5084120.html

bzoj 1729: [Usaco2005 dec]Cow Patterns 牛的模式匹配

  正解似乎是kmp。。。具体见网上题解

  然而我偷懒写了hash= =。。。结果卧槽又长又慢

  如何判断网上题解挺详细的。。。然而事实告诉我们其实并不用那么严格。。选几个类似(并好维护些)的指标(比方说轮廓啊什么的)。。符合的话就再随机几个点确定一下是不是真的全都符合= =。。。。这样大概就可以了吧

  真相是看网上题解时几个字看错TAT。。结果拍了半天。。最后直接再rand几个数判断= =。。写了4个hashTAT

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#define ll long long
#define ull unsigned int
using namespace std;
const int maxn=;
const int maxk=;
int sum[],num[],sumb[],tmpsum[],bel[],belb[],pre1[],pre2[],pre0[];
int b[maxk],preb[maxk];
int a[maxn],ans[maxn],prea[maxn],presm[maxn],prebg[maxn];
int i,j,k,n,m,judlen,s;
int jud[];
ull jc1[maxk];
bool u[],u1[maxk];
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
struct zs{
int too,pre;
}e0[maxn],e1[maxn],e2[maxn];
int l0[maxn],l1[maxn],l2[maxn],t0,t1,t2;
ull nowh1=,nowh2=,nowh0=;
ull hash=,hnow=,jc,h1=,h2=,h0=;
int kindb,kind;
inline void ins0(int a,int b){e0[++t0].too=b;e0[t0].pre=l0[a];l0[a]=t0;}
inline void ins1(int a,int b){e1[++t1].too=b;e1[t1].pre=l1[a];l1[a]=t1;}
inline void ins2(int a,int b){e2[++t2].too=b;e2[t2].pre=l2[a];l2[a]=t2;}
inline void clr(int x){
int i,to;
for(i=l0[x];i;i=e0[i].pre){
to=e0[i].too;prea[to]=;if(to>x+k)continue;
nowh0-=(ull)(to-x)*jc1[x+k-to];
}
for(i=l1[x];i;i=e1[i].pre){
to=e1[i].too;presm[to]=;if(to>x+k)continue;
nowh1-=(ull)(to-x)*jc1[x+k-to];
}
for(i=l2[x];i;i=e2[i].pre){
to=e2[i].too;prebg[to]=;if(to>x+k)continue;
nowh2-=(ull)(to-x)*jc1[x+k-to];
}
}
ull mphash=,mphashb[],jjc;
int main(){
srand();jc=jc1[]=;
n=read();k=read();s=read();
judlen=min(k,min(,/n));
for(i=;i<=judlen;i++){
for(j=rand()%k;u1[j];j=rand()%k);
jud[i]=j;u1[j]=;
}
for(i=;i<=n;i++)a[i]=read();
for(i=;i<=k;i++){
b[i]=read();u[b[i]]=;
if(i>){hash*=;hash+=b[i]>=b[i-]?:;jc*=;}
presm[i]=pre1[b[i]];prebg[i]=pre2[b[i]];preb[i]=pre0[b[i]]; for(j=;j<b[i];j++)pre2[j]=i;for(j=b[i]+;j<=s;j++)pre1[j]=i;pre0[b[i]]=i; h1*=;if(presm[i])h1+=i-presm[i];
h2*=;if(prebg[i])h2+=i-prebg[i];
h0*=;if(preb[i])h0+=i-preb[i];jc1[i]=jc1[i-]*;
}
for(i=;i<=s;i++)if(u[i])kindb++,belb[i]=kindb;
for(i=;i<=k;i++)b[i]=belb[b[i]],sumb[b[i]]++;
memset(pre1,,sizeof(pre1));memset(pre2,,sizeof(pre2));memset(pre0,,sizeof(pre0));
for(i=;i<=n;i++){
presm[i]=pre1[a[i]];prebg[i]=pre2[a[i]];prea[i]=pre0[a[i]]; pre0[a[i]]=i;for(j=;j<a[i];j++)pre2[j]=i;for(j=a[i]+;j<=s;j++)pre1[j]=i;pre0[a[i]]=i;
ins0(prea[i],i);ins1(presm[i],i);ins2(prebg[i],i);
}
for(i=;i<=k;i++){
if(i>){hnow*=;hnow+=a[i]>=a[i-]?:;}
kind+=!sum[a[i]],sum[a[i]]++;
nowh1*=;if(presm[i])nowh1+=i-presm[i];
nowh2*=;if(prebg[i])nowh2+=i-prebg[i];
nowh0*=;if(prea[i])nowh0+=i-prea[i];
}
int tmp;jjc=;
for(i=;i<=k;i++){
for(j=;j<;j++)mphashb[j]*=,mphashb[j]+=b[i]+j-;
mphash*=,mphash+=a[i];jjc*=;
}
int val[]={,};
for(i=k;i<=n;
i++,hnow*=,hnow+=val[a[i]>=a[i-]],hnow-=jc*val[a[i-k+]>=a[i-k]]
,sum[a[i-k]]--,kind-=(!sum[a[i-k]])-(!sum[a[i]]),sum[a[i]]++
,nowh1*=,nowh1+=presm[i]?i-presm[i]:
,nowh2*=,nowh2+=prebg[i]?i-prebg[i]:
,nowh0*=,nowh0+=prea[i]?i-prea[i]:
,clr(i-k)
,mphash*=,mphash+=a[i],mphash-=jjc*a[i-k]
)
if(hnow==hash&&kind==kindb&&nowh1==h1&&nowh0==h0&&nowh2==h2){
bool f1=;
for(j=;j<;j++)if(mphash==mphashb[j]){ans[++ans[]]=i-k+;f1=;break;}
if(!f1)continue;
for(j=;j<;j++)if(mphash==mphashb[j]){ans[++ans[]]=i-k+;f1=;break;}
if(!f1)continue;
for(tmp=,j=;j<=s;j++)if(sum[j])if(sum[j]!=sumb[++tmp]){f1=;break;}
else bel[j]=tmp;
if(!f1)continue;
for(j=;j<=judlen;j++)if(bel[a[i-jud[j]]]!=b[k-jud[j]]){f1=;break;}
if(!f1)continue;
ans[++ans[]]=i-k+;
}
printf("%d\n",ans[]);
for(i=;i<=ans[];i++)printf("%d\n",ans[i]);
return ;
}

bzoj 1780: [Usaco2010 Feb]corral 覆盖牛棚

  首先把被完全覆盖的围栏去掉,剩下的按左端点升序排序。那么右端点肯定也是升序的了。。然后计算出每段围栏,它接下去一段围栏可达到的最远距离。

  枚举起点,贪心地一段一段接下去就可得到该起点的最优解。

  直接这样做显然会T。。我们找出一圈围栏后,把这些围栏都指向刚好跨过一圈的那条围栏就可以避免同个解不同起点的重复计算了(看了kpm代码才会的QAQ)。

  复杂度不会算...

大概是#1吧。。。bzoj usaco 金组水题题解(2.5)

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
struct fence{
int l,r;
}fen[maxn];
int next[maxn],pos[maxn],jump[maxn],tmpcos[maxn];
int i,j,L,n,m,ans,k;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
bool cmp1(fence a,fence b){return (a.l<b.l)||(a.l==b.l&&a.r>b.r);}
inline void get(int x){
if(jump[x])return;
if(fen[x].r>=L)jump[x]=x,tmpcos[x]=;else get(next[x]),jump[x]=jump[next[x]],tmpcos[x]=tmpcos[next[x]]+;
}
int main(){
L=read();n=read();
for(i=;i<=n;i++)fen[i].l=read(),fen[i].r=read()+fen[i].l;
sort(fen+,fen++n,cmp1);
int mx=-;int tmp=;
for(i=;i<=n;i++)if(fen[i].r>mx)fen[++tmp]=fen[i],mx=fen[i].r;n=tmp;
tmp=;
for(i=;i<=n;i++){
next[i]=next[i-];
for(;tmp<n&&fen[tmp+].l<=fen[i].r;tmp++);
if(tmp==i)next[i]=;else next[i]=tmp,pos[i]=fen[tmp].r;
}
int tmpsum,now;
ans=;
for(i=;i<=n;i++){
if(!jump[i])get(i);tmpsum=tmpcos[i]+;now=jump[i];
while(next[now]&&fen[now].r<fen[i].l+L&&tmpsum<ans)now=next[now],tmpsum++;
if(fen[now].r>=fen[i].l+L&&tmpsum<ans)ans=tmpsum;
}
printf("%d\n",ans);
return ;
}

  正常做法的话大概就是 每个围栏 向 它接下去最远的围栏连条有向边,然后就是基环内向树上的询问了?

bzoj 1698: [Usaco2007 Feb]Lilypad Pond 荷叶池塘

  最短路计数。。题解说得挺详细的。。

  一开始想到把连到空格子的边权设为1,结果发现这样会重复计算方案(兜了一圈边权0的再回来,放的荷叶并没有变化)

  那么根据网上的题解TAT,处理出两两空格子(包括起点和终点)之间能否不经其他空格子到达。。可以到达的话就连边,权值为1。

  在新图中跑最短路计数就行了。KPM实力#1。。。伏地膜

  注意因为新图中的边权都为1所以最短路计数随便搞。。。如果是一般些的图的话得考虑重复计数的情况。

 #include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=;
const int xx[]={-,-,,,,,-,-},yy[]={-,-,-,-,,,,};
struct zs{
int too,pre;
}e[maxn*maxn*maxn*maxn];
int map[maxn][maxn];
int id[maxn][maxn],poi;
int last[maxn*maxn],tot;
int l,r,now,i,j,k,nowx,nowy,x,y,n,m,a,S,T,to;
int dlx[maxn*maxn],dly[maxn*maxn],dis[maxn*maxn];
ll num[maxn*maxn];
bool u[maxn*maxn];
inline void insert(int a,int b){
e[++tot].too=b;e[tot].pre=last[a];last[a]=tot;
// printf(" %d-->%d\n",a,b);
} int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
n=read();m=read();
for(i=;i<=n;i++)for(j=;j<=m;j++){
a=read(),id[i][j]=++poi,map[i][j]=(a==||a==||a==)?:a;
if(a==)S=poi;if(a==)T=poi;
}
for(i=;i<=n;i++)for(j=;j<=m;j++)if(!map[i][j]){
memset(u,,poi+);
l=;r=;dlx[]=i;dly[]=j;u[id[i][j]]=;
while(l<r){
nowx=dlx[++l];nowy=dly[l];
for(k=;k<;k++){
x=nowx+xx[k];y=nowy+yy[k];
if(x<||y<||x>n||y>m||u[id[x][y]]||map[x][y]==)continue;u[id[x][y]]=;
if(map[x][y])dlx[++r]=x,dly[r]=y;else insert(id[i][j],id[x][y]);
}
}
}
memset(u,,poi+);
l=;r=;dlx[]=S;num[S]=;dis[S]=;
while(l<r){
now=dlx[++l];u[now]=;
if(now==T)break;
for(i=last[now],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(!u[to])
if(!num[to])dlx[++r]=to,num[to]=num[now],dis[to]=dis[now]+;
else if(dis[to]==dis[now]+)num[to]+=num[now];
}
if(num[T])printf("%d\n",dis[T]-),printf("%lld\n",num[T]);
else puts("-1");
// printf(" %d\n",tot);
return ;
}

bzoj 1737: [Usaco2005 jan]Naptime 午睡时间

  dp。。设f[i][j][0]表示前i个时间段里,睡了j个时间段的最大值,但时间i没睡。f[i][j][1]表示时间i睡了,其他一样。val[i]表示在时间i睡觉的总效用值。

  f[i][j][0]=max{ f[i-1][j][0],f[i-1][j][1] }

  f[i][j][1]=max{ f[i-1][j-1][0],f[i-1][j-1][1]+val[i] }。

  第一次我们强制不跨环,初始化f[1][1][1]=f[1][0][0]=0,此时答案是f数组最大值;第二次我们强制跨环,初始化f[1][1][1]=0,f[1][0][0]=-inf,此时答案是f[n][m][1]+val[1]。

  最终答案就是两种情况的较大值。

又是#1辣。。bzoj usaco 金组水题题解(2.5)

 #include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=;
int f[maxn][],pre;
int val[maxn];
int i,j,n,m,ans;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
n=read();m=read();
for(i=;i<=n;i++)val[i]=read();
memset(f,,(m+)<<);
f[][]=f[][]=;
for(i=;i<=n;i++)for(j=m;j;j--){
if(f[j][]>f[j][])f[j][]=f[j][];
f[j][]=f[j-][]+val[i];if(f[j-][]>f[j][])f[j][]=f[j-][];
}ans=max(f[m][],f[m][]);
memset(f,,(m+)<<);
f[][]=;
for(i=;i<=n;i++)for(j=m;j;j--){
if(f[j][]>f[j][])f[j][]=f[j][];
f[j][]=f[j-][]+val[i];if(f[j-][]>f[j][])f[j][]=f[j-][];
}ans=max(ans,f[m][]+val[]);
printf("%d\n",ans);
return ;
}

bzoj 3312: [Usaco2013 Nov]No Change

  一开始以为是贪心。。结果看数据范围果断状压= =

  f[i]表示已取硬币状态为i时,最多可支付的东西数。pre[i]表示前i个东西的价格和。val[i]表示第i个硬币的面值

  f[i]=max{ f[i-(1<<k)]+x },((1<<k)存在于状态i中,x是使pre[ x+f[i-(1<<k) ]-pre[ f[i-(1<<k)] ] <=val[k]的最大值,也就是最多能再买多少件东西  )。

  时间复杂度(2^k*k*logn)

 #include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=;
int pre[maxn],val[],two[];
int f[(<<)+],pos[(<<)+];
int i,j,K,n,l,r,mid,k,st,tmp,ans;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
K=read();n=read();two[]=;pos[]=;
for(i=;i<K;i++)val[i]=read();
for(i=;i<K;i++)two[i]=two[i-]<<,pos[two[i]]=i;
for(i=;i<=n;i++)pre[i]=read()+pre[i-];
f[]=;int mx=<<K;
ans=-;
for(i=;i<mx;i++){
for(tmp=,k=mx-i-,j=pos[k&-k];k;k-=k&-k,j=pos[k&-k])tmp+=val[j];
if(tmp<=ans)continue;
for(k=i,j=pos[k&-k];k;k-=k&-k,j=pos[k&-k]){
l=st=f[i^(k&-k)];r=n;
while(l<r&&r>f[i]){
mid=(l+r+)>>;
if(pre[mid]-pre[st]>val[j])r=mid-;else l=mid;
}
if(l>f[i])f[i]=l;
}
if(f[i]==n)ans=tmp;
// printf(" %d %d\n",i,f[i]);
}
printf("%d\n",ans);
return ;
}

bzoj 1750: [Usaco2005 qua]Apple Catching

  傻逼dp。。f[i][j][k]表示i秒后,牛已移动j次,在k处(k=0在左,k=1在右)

  f[i][j][k]=max{ f[i-1][j][k],f[i-1][j-1][1-k] }+map[i][k],(map[i][k]表示k处在第i秒的时候有无苹果。)

 #include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int f[][];
int i,j,x,n,m,ans;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
n=read();m=read();
f[][]=-;
for(i=;i<=n;i++){
x=read();x--;
if(x)for(j=m;j;j--){
if(f[j-][]>f[j][])f[j][]=f[j-][];
if(f[j-][]>f[j][])f[j][]=f[j-][];
f[j][]++;
}else for(j=m;j;j--){
if(f[j-][]>f[j][])f[j][]=f[j-][];
if(f[j-][]>f[j][])f[j][]=f[j-][];
f[j][]++;
}
f[j][x]++;
}
ans=max(f[m][],f[m][]);
for(i=m-;i>=;i--){
if(f[i][]>ans)ans=f[i][];
if(f[i][]>ans)ans=f[i][];
}
printf("%d\n",ans);
return ;
}

bzoj 1746: [Usaco2005 open]Lazy Cows

  再次验证了我是傻逼= =

  因为只有两行,所以帆布的状态只有四种:只覆盖上面一行,只覆盖下面一行,两行用一块布覆盖,两行用两块布分别覆盖。

  用f[i][j][0..3]表示用j块布覆盖了前i个洞后,对应以上四种状态的最小面积。预处理pos[i]表示第i个破洞的列数。

  f[i][0]=min(  min{ f[i-1][j][0],f[i-1][j][3] }+pos[i]-pos[i-1]  ,  min{ f[i-1][j-1][0..3] }+1  ),(第i个洞在第一行)

  f[i][1]基本同上。

  f[i][2]=min{  f[i-1][j][2]+2*(pos[i]-pos[i-1])  ,  min{ f[i-1][j-1][0..3] }+2  }

  f[i][3]=min{  min{ f[i-1][j-1][0..1] }+pos[i]-pos[i-1]+1  ,  min{ f[i-1][j-2][0..3] }+2  ,  f[i-1][j][3]+2*(pos[i]-pos[i-1])  }

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
const int inf=;
struct poi{
bool x;int y;
}a[maxn];
int num[maxn],dis[maxn];
int f[maxn][];
int n,m,flag,K,mn,ans;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
bool cmp(poi a,poi b){return a.y<b.y;}
int main(){
int i,j,now;
n=read();K=read();m=read();
for(i=;i<=n;i++)a[i].x=read()==,a[i].y=read();
sort(a+,a++n,cmp);int tmp=;a[].y=a[].y;
for(i=;i<=n;){
flag=a[i].x+;
for(j=i+;j<=n&&a[j].y==a[i].y;j++)flag|=a[j].x+;
num[++tmp]=flag-;
dis[tmp]=a[i].y-a[i-].y;
i=j;
}n=tmp;
memset(f,,(n+)<<);
f[][num[]]=max(num[],);f[][]=f[][]=;
for(i=;i<=n;i++)for(now=dis[i],j=K-,mn=min(f[j][],f[j][]<f[j][]?f[j][]:f[j][]),f[++j][]+=now,f[j][]+=now,f[j][]+=now,f[j][]+=now;j;){
if(!num[i]){
if(mn<f[j][])f[j][]=mn+;
if(f[j][]<f[j][])f[j][]=f[j][];
f[j][]=inf;
}else if(num[i]==){
if(mn<f[j][])f[j][]=mn+;
if(f[j][]<f[j][])f[j][]=f[j][];
f[j][]=inf;
}else f[j][]=f[j][]=inf; f[j][]+=now;if(mn+<f[j][])f[j][]=mn+;
f[j][]+=now;
j-=;if(j>=){
mn=f[j][];if(f[j][]<mn)mn=f[j][];if(f[j][]<mn)mn=f[j][];if(f[j][]<mn)mn=f[j][];
if(mn+<f[j+][])f[j+][]=mn+;
}
f[++j][]+=now;f[j][]+=now;f[j][]+=now;f[j][]+=now;
if(f[j][]<f[j+][])f[j+][]=f[j][]+;
if(f[j][]<f[j+][])f[j+][]=f[j][]+;
}
ans=inf;
i=min(n,K);
for(j=;j<;j++)if(f[i][j]<ans)ans=f[i][j];
printf("%d\n",ans);
return ;
}

bzoj 1727: [Usaco2006 Open]The Milk Queue 挤奶队列

  抱了ctl学长的大腿。。。

  贪心。。如果牛A排在牛B前面比B排在A前面更优所以blabla。。具体列个式子后快排就好了。。

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=;
int id[maxn],a[maxn],b[maxn];
int i,end1,end2,j,n;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
bool cmp(int x,int y){return a[x]+b[y]+(b[x]>a[y]?b[x]:a[y])<a[y]+b[x]+(b[y]>a[x]?b[y]:a[x]);}
int main(){
n=read();
for(i=;i<=n;i++)a[i]=read(),b[i]=read(),id[i]=i;
sort(id+,id++n,cmp);
for(i=;i<=n;i++){
j=id[i];end1+=a[j];
if(end1<=end2)end2+=b[j];else end2=end1+b[j];
}
printf("%d\n",max(end1,end2));
return ;
}

bzoj 1736: [Usaco2005 jan]The Wedding Juicer 婚宴的榨汁机

  从图的边界开始往内部跑类似最短路的。。dis[i]表示i点最大可接受高度。

  dis[i]也就是从图的边界上的任意点出发,高度不递减地跑到i,途中经过的最大高度的最小值(就是水流出去的过程反过来)。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int maxn=;
const int xx[]={,,,-},yy[]={,-,,};
int map[maxn][],num[maxn],poinum,id[][];
int dis[maxn],mp[maxn];
bool used[maxn],full[maxn];
int i,j,k,n,m,nx,ny,x,y,mx,now,size,to;
ll ans; struct poi{int pos,dis;};
priority_queue<poi>q;
bool operator <(poi a,poi b){return a.dis>b.dis;} int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
m=read();n=read();
for(i=;i<=n;i++)for(j=;j<=m;j++)id[i][j]=++poinum,mp[poinum]=read(),mx=max(mx,mp[poinum]);
for(i=;i<=n;i++)for(j=;j<=m;j++)for(k=,x=id[i][j];k<;k++){
nx=i+xx[k];ny=j+yy[k];if(nx<||nx>n||ny<||ny>m)continue;
y=id[nx][ny];map[x][num[x]++]=y;
}
for(i=;i<=n;i++)for(j=;j<=m;j++)
if(i==||i==n||j==||j==m)dis[id[i][j]]=mp[id[i][j]],full[id[i][j]]=;
else dis[id[i][j]]=mx;
for(i=;i<m;i++)q.push((poi){i,mp[i]}),q.push((poi){id[n][i],mp[id[n][i]]}),size+=;
for(i=;i<=n;i++)q.push((poi){id[i][],mp[id[i][]]}),q.push((poi){id[i][m],mp[id[i][m]]}),size+=;
while(size){
while(size&&used[q.top().pos])q.pop(),size--;
if(!size||q.top().dis==mx)break;
now=q.top().pos;x=q.top().dis;used[now]=;q.pop();size--;
for(i=;i<num[now];i++)if(dis[to=map[now][i]]>x&&!full[to]){
if(mp[to]>x)dis[to]=mp[to],full[to]=;else dis[to]=x;
q.push((poi){to,dis[to]});size++;
}
}
for(i=;i<=n;i++)for(j=;j<=m;j++)if(!full[id[i][j]])ans+=(ll)dis[id[i][j]]-mp[id[i][j]];
printf("%lld\n",ans);
return ;
}

bzoj 3408: [Usaco2009 Oct]Heat Wave 热浪

  裸最短路。。。为何这么少人写

  又一次调优先队列结果和正常的spfa一样快。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct zs{int too,pre,dis;}e[];
struct poi{int pos,dis;};
priority_queue<poi>q;
bool operator <(poi a,poi b){return a.dis>b.dis;}
int last[],dis[];
bool used[];
int i,j,s,t,a,b,x,y,z,n,m,tot,size,to;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
int main(){
n=read();m=read();s=read();t=read();
for(;m;m--){
a=read(),b=read();
e[++tot].dis=e[tot+].dis=read();
e[tot].too=b;e[tot].pre=last[a];last[a]=tot;
e[++tot].too=a;e[tot].pre=last[b];last[b]=tot;
}
memset(dis,,(n+)<<);dis[s]=;size=;q.push((poi){s,});
while(size&&!used[t]){
while(size&&used[q.top().pos])q.pop(),size--;
if(!size||dis[q.top().pos]>=dis[t]||used[t])break;
x=q.top().pos;y=q.top().dis;q.pop();--size;used[x]=; for(to=e[i=last[x]].too,z=y+e[i].dis;i;to=e[i=e[i].pre].too,z=y+e[i].dis)if(dis[to]>z&&z<dis[t])
q.push((poi){to,dis[to]=z}),size++;
}
printf("%d\n",dis[t]);
return ;
}

bzoj 2590: [Usaco2012 Feb]Cow Coupons

  排序显然是错的= =懒得重新写了>_<

  如果没有优惠券的话直接排序后贪心。有了优惠券后,把每头牛拆成有优惠券和没优惠券两种,把2n个数排序,注意如果买了一头优惠的牛,把它没优惠券的版本设为不可取。

  无法理解为啥sort比kpm调优先队列慢TAT(虽然要排2n个数。。但常数应该是比优先队列小很多的吧。。)

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
bool u[];
int id[],a[];
int i,j,n,k,ans,n1;
ll m;
int ra;char rx;
inline int read(){
rx=getchar();ra=;
while(rx<''||rx>'')rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra;
}
bool cmp(int x,int y){return a[x]<a[y];}
int main(){
n=read();k=read();scanf("%lld",&m);
for(i=;i<=n;i++)id[i]=i,id[i+n]=i+n,a[i]=read(),a[i+n]=read();
n1=n<<;
sort(id+,id++n1,cmp);
for(i=,j=id[];i<=n1&&m>;j=id[++i])if(m>=a[j]&&!u[j>n?j-n:j]&&(j<=n||k))
m-=a[j],u[j>n?j-n:j]=,ans++,k-=j>n;
printf("%d\n",ans);
return ;
}

完结撒花[鼓掌熊]总共大概106道吧。。