zoukankan      html  css  js  c++  java
  • [2020.2.19]codeforces1307 Codeforces Round #621 (Div. 1 + Div. 2)

    回想起上次写博客,好像已经是去年的事一样

    概况

    排名:179/7155

    过题数:5

    Rating:(color{green}{+74})((color{orange}{2263}))

    题目

    A. Cow and Haybales

    AC时间:4min,492分

    题解:

    显然先把所有在位置2的移到1,再把在位置3的移到一,以此类推直到天数不足。

    直接模拟即可。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    int T,n,d,a[110],ans,t;
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d",&n,&d),ans=0;
    		for(int i=1;i<=n;++i)scanf("%d",&a[i]),t=min(a[i],i!=1?d/(i-1):100),ans+=t,d-=(i-1)*t;
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    B.Cow and Friend

    AC时间:12min,952分

    题解:

    注意到两步可以在横轴上跳出(0)(2max(a_i))的任意距离。

    那么答案至多是(lceilfrac{x}{2max(a_i)} ceil)

    或者答案为奇数,那么就是(lceilfrac{x-max(a_i)}{2max(a_i)} ceil+1)

    比赛代码写得太zz,就不放了。

    C.Cow and Message

    AC时间:21min,1374分

    题解:

    其实secret message长度一定是1或2,因为任何长度大于2的子串出现次数都小于等于其长度为2的前缀。

    于是考虑如何计算一个长度为2的字符串的出现次数。

    枚举第一个字符,然后顺序枚举字符串。记录之前第一个字符出现的次数,在第(i)位将以(s_i)为结尾的串数量加上之前第一个字符的出现次数即可。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    int n,apn;
    long long ans,tot[30];
    char s[100010];
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	for(int i=0;i<26;++i){
    		apn=0;
    		for(int j=0;j<26;++j)tot[j]=0;
    		for(int j=1;j<=n;++j)tot[s[j]-'a']+=apn,apn+=(s[j]-'a'==i);
    		for(int j=0;j<26;++j)ans=max(ans,tot[j]);
    		ans=max(ans,1ll*apn);
    	}
    	printf("%I64d",ans);
    	return 0;
    }
    

    D.Cow and Fields

    AC时间:54min,1518分(-1)

    题解:

    两遍bfs求出每个点与(1)(n)的距离,记作(d_{1,i})(d_{n,i})

    那么如果我们在(u,v)之间连边,最短路会对(min(d_{1,u}+d_{n,v},d_{n,u}+d_{1,v})+1)(min)

    于是我们将特殊点到(1,n)的距离分别排序,然后二分判断最短路长度(ge x)是否可行。

    假设以到(1)的距离和到(n)的距离从小到大排序后的特殊点分别为(a_1,a_2,...,a_k)(b_1,b_2,...,b_k)

    那么对于任何一个(a_i),使得(d_{1,a_i}+d_{n,b_j}+1ge x)(j)一定是一段后缀,而且如果(i_1le i_2),那么(i_1)所对应的后缀一定不短于(i_2)所对应的后缀。

    那么我们倒序枚举(a_i),同时维护所对应后缀中,(d_{1,b_i})的最大值和次大值。

    然后如果存在(a_i),使得(d_{n,a_i}+mxd_i+1ge x),那么最短路长度为(x)可行。其中若后缀最大值取到(d_{1,a_i}),那么(mxd_i)为后缀次大值,否则为最大值。

    code:

    #include<bits/stdc++.h>
    #define ci const int&
    using namespace std;
    struct edge{
    	int t,nxt;
    }e[400010];
    struct Val{
    	int dis,id;
    }t1[200010],tn[200010];
    int n,m,k,a[200010],u,v,t,be[200010],cnt,d1[200010],dn[200010],f1,v1,v2,cv,l,r,mid;
    queue<int>q;
    void add(ci x,ci y){
    	e[++cnt].t=y,e[cnt].nxt=be[x],be[x]=cnt;
    }
    bool cmp(Val x,Val y){
    	return x.dis>y.dis;
    }
    void bfs(int*dis,ci st){
    	dis[st]=1,q.push(st);
    	while(!q.empty()){
    		t=q.front(),q.pop();
    		for(int i=be[t];i;i=e[i].nxt)!dis[e[i].t]?q.push(e[i].t),dis[e[i].t]=dis[t]+1:0;
    	}
    	for(int i=1;i<=n;++i)--dis[i];
    }
    bool Check(ci x){
    	int tl=0;
    	f1=0,v1=v2=-1;
    	for(int i=k;i>=1;--i){
    		while(tl<k&&t1[i].dis+tn[tl+1].dis+1>=x){
    			++tl,t=tn[tl].id;
    			if(d1[t]>v1)v2=v1,v1=d1[t],f1=t;
    			else if(d1[v]>v2)v2=d1[v];
    		}
    		if(!tl||(tl==1&&f1==t1[i].id))continue;
    		cv=(f1==t1[i].id?v2:v1);
    		if(dn[t1[i].id]+cv+1>=x)return true;
    	}
    	return false;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	for(int i=1;i<=k;++i)scanf("%d",&a[i]);
    	for(int i=1;i<=m;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
    	bfs(d1,1),bfs(dn,n);
    	for(int i=1;i<=k;++i)t1[i]=(Val){d1[a[i]],a[i]},tn[i]=(Val){dn[a[i]],a[i]};
    	sort(t1+1,t1+k+1,cmp),sort(tn+1,tn+k+1,cmp);
    	l=1,r=d1[n];
    	while(l<r)mid=l+r+1>>1,Check(mid)?l=mid:r=mid-1;
    	printf("%d",l);
    	return 0;
    }
    

    E.Cow and Treats

    AC时间:2h5min,1200分

    题解:

    首先,喜好相同的牛至多只能出现2头,而且若出现了两头,一定在不同的方向上,也就是一个方向上的牛一定是喜好互不相同的,也就是说每一头牛吃哪些草是固定的。

    如果一头牛需要的草的数量大于这种草的总量,可以直接将其删除。

    同时,容易发现对于一种将一些牛分入左右两个集合的方式,使其合法的排队方式要么不存在,要么唯一。

    所以我们可以不考虑牛之间的相互顺序,只考虑它是否被加入集合,以及在左边还是右边。

    那么,我们对于每一头牛预处理其从左往右和从右往左时会在哪里睡下,记为(pl,pr)

    然后,我们枚举从左向右走的牛最远走到了哪里,记为(r),计算此时最多的牛的数量和方案数。

    我们再枚举牛的种类,然后分类讨论:

    如果这种牛正好喜欢(r)处的草,那么如果不存在一头牛使得(pl=r),那么显然无解。

    否则考虑是否存在(pl eq r,pr>r)的牛,如果不存在,最多牛的数量+1,方案数不变。

    如果存在,最多牛的数量+2,方案数乘上这种牛的数量。

    然后考虑这种牛不喜欢(r)处的草,如果不存在(pl<r)(pr>r)的牛,那么这种牛不会对答案造成任何贡献。

    如果不能找出两头不同的牛,使得第一头满足(pl<r)且第二头满足(pr>r),那么最多牛的数量+1,方案数乘上满足(pl<r)的牛的数量和(pr>r)的牛的数量的和。

    否则,最多牛的数量+2,记上一次的方案数为(lst),然后枚举那一头牛是第一头牛,答案加上(lst imes)能够作为第二头牛的牛数(也就是除去第一头牛外满足(pr>r)的牛的数量)。

    最后,还要考虑没有牛在左侧的情况。比较简单就不讲了。

    code:

    #include<bits/stdc++.h>
    #define ci const int&
    using namespace std;
    const int mod=1e9+7;
    struct cw{
    	int sl,sr;
    }tmp;
    int n,m,u,v,s[5010],wy[2],tg,tt,numc,tag,ans1,ans2;
    vector<cw>c[5010];
    bool cmp(cw x,cw y){
    	return x.sl<y.sl;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i)scanf("%d",&s[i]);
    	for(int i=1;i<=m;++i){
    		scanf("%d%d",&u,&v);
    		tmp.sl=1,tmp.sr=n,tt=0;
    		while(tmp.sl<=n&&(tt+=(s[tmp.sl]==u))<v)++tmp.sl;
    		if(tmp.sl>n)continue;
    		tt=0;
    		while(tmp.sr>=1&&(tt+=(s[tmp.sr]==u))<v)--tmp.sr;
    		if(tmp.sr<1)continue;
    		c[u].push_back(tmp);
    	}
    	for(int i=1;i<=n;++i)if(c[i].size())sort(c[i].begin(),c[i].end(),cmp);
    	for(int i=1;i<=n;++i)if(c[s[i]].size()&&c[s[i]][0].sl<=i){
    		tt=0,wy[tg]=1;
    		for(int j=1;j<=n&&wy[tg];++j)if(c[j].size()){
    			tg^=1,tag=numc=wy[tg]=0;
    			if(j!=s[i]){
    				for(int k=0;k<c[j].size();++k)numc+=(c[j][k].sr>i);
    				if(!numc&&c[j][0].sl>i){
    					tg^=1;
    					continue;
    				}
    				++tt;
    				for(int k=0;k<c[j].size()&&c[j][k].sl<i;++k){
    					if(!tag&&numc-(c[j][k].sr>i))tag=1,wy[tg]=0;
    					wy[tg]=(wy[tg]+1ll*wy[tg^1]*max(1,numc-(c[j][k].sr>i)))%mod;
    				}
    				if(!tag)wy[tg]=(wy[tg]+1ll*wy[tg^1]*numc)%mod;
    				tt+=tag;
    			}else{
    				++tt;
    				for(int k=0;k<c[j].size();++k)numc+=(c[j][k].sr>i&&c[j][k].sl!=i),tag|=(c[j][k].sl==i);
    				if(!tag)goto End;
    				wy[tg]=1ll*wy[tg^1]*max(1,numc)%mod,tt+=!!numc;
    			}
    		}
    		if(!wy[tg]||tt<ans1)continue;
    		if(tt>ans1)ans1=tt,ans2=wy[tg];
    		else if(tt==ans1)(ans2+=wy[tg])>=mod?ans2-=mod:0;
    		End:;
    	}
    	//NO L
    	tt=0,wy[tg]=1;
    	for(int i=1;i<=n;++i)if(c[i].size())++tt,wy[tg]=1ll*wy[tg]*c[i].size()%mod;
    	if(tt>ans1)ans1=tt,ans2=wy[tg];
    	else if(tt==ans1)(ans2+=wy[tg])>=mod?ans2-=mod:0;
    	printf("%d %d",ans1,ans2);
    	return 0;
    }
    

    G.Cow and Exercise(赛后)

    题解:

    考虑对于每组询问二分答案(mid),计算此时最少需要多少代价。

    我们可以给每一个点一个值(d_u),使得(dis(1,u)=d_u-d_1)

    那么根据最短路的性质,对于每条边((u,v))我们有

    (d_vle d_u+w_{u,v}+x_{u,v}),其中(w_{u,v})为边((u,v))一开始的边权,(x_{u,v})为我们加的边权。

    同时我们有(d_n-d_1ge mid),然后我们要最小化(sum_{(u,v)}x_{u,v})

    经过简单的转化,我们有

    (x_{u,v}+d_u-d_vge-w_{u,v})

    (d_n-d_1ge mid)

    然后发现这个问题的对偶问题为一个最大费用循环流问题,即:

    (u,v)之间连上容量为(1),费用为(-w_{u,v})的边,在(t,1)之间连上容量没有限制,费用为(mid)的边,求最大费用循环流。

    但是如果对于每一个mid都建图跑网络流显然是要T的。

    我们发现求这个网络的最大费用循环流相当于:

    (u,v)之间连上容量为(1),费用为(w_{u,v})的边,在这个图中求一个流,设流量为(f),费用为(c),求(mid imes f-c)的最大值。

    于是,我们考虑对于每一个(f)求出达到这个流量的最小费用。

    参考最小费用最大流的过程,若我们以(c)为横坐标,最小的(f)为纵坐标,图像应该长成一个连续的分段函数,每一段都是一个一次函数,第(i)段的斜率正是第(i)次增广的路径的费用,同时该斜率是不降的。

    于是我们对这个网络跑一次费用流,记录每次增广的单位花费(cost_i),此时网络的总流量(f_i),此时的总花费(C_i)。对于每个(mid),可以直接找到最大的(cost_i<mid),此时的(mid imes f_i-C_i)就是最大费用循环流,也即使原图最短路长度不小于(mid)的最小花费。

    code:

    #include<bits/stdc++.h>
    #define REV(x) (x&1?x+1:x-1)
    #define ci const int&
    using namespace std;
    const int INF=1e9;
    struct edge{
    	int t,nxt,f,c;
    }e[5010];
    int n,m,Q,u,v,w,be[55],cnt,sz,fv[2510],fl[2510],dis[55],iq[55],ls[55],fe[55],t,lf;
    double l,r,mid;
    long long sv[2510];
    queue<int>q;
    void add(ci x,ci y,ci fl,ci cs){
    	e[++cnt]=(edge){y,be[x],fl,cs},be[x]=cnt;
    }
    bool SPFA(){
    	for(int i=2;i<=n;++i)dis[i]=INF;
    	q.push(1),iq[1]=1;
    	while(!q.empty()){
    		iq[t=q.front()]=0,q.pop();
    		for(int i=be[t];i;i=e[i].nxt)if(e[i].f&&dis[e[i].t]>dis[t]+e[i].c){
    			dis[e[i].t]=dis[t]+e[i].c,ls[e[i].t]=t,fe[e[i].t]=i;
    			if(!iq[e[i].t])q.push(e[i].t),iq[e[i].t]=1;
    		}
    	}
    	if(dis[n]>=INF)return false;
    	t=0,lf=INF;
    	for(int i=n;i!=1;i=ls[i])lf=min(lf,e[fe[i]].f);
    	for(int i=n;i!=1;i=ls[i])e[fe[i]].f-=lf,e[REV(fe[i])].f+=lf,t+=e[fe[i]].c;
    	if(!sz||fv[sz]!=t)fv[++sz]=t,sv[sz]=sv[sz-1]+lf*t,fl[sz]=fl[sz-1]+lf;
    	else sv[sz]+=lf*t,fl[sz]+=lf;
    	return true;
    }
    bool Check(double x){
    	int tl=1,tr=sz,tm;
    	while(tl<tr)tm=tl+tr+1>>1,fv[tm]<x?tl=tm:tr=tm-1;
    	return fl[tl]*x-sv[tl]<=w;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;++i)scanf("%d%d%d",&u,&v,&w),add(u,v,1,w),add(v,u,0,-w);
    	while(SPFA());
    	scanf("%d",&Q);
    	while(Q--){
    		scanf("%d",&w);
    		l=1,r=1e26;
    		while(r-l>1e-7)mid=(l+r)/2.0,Check(mid)?l=mid:r=mid;
    		printf("%.10lf
    ",l);
    	}
    	return 0;
    }
    

    总结

    现场做出G就赢了/kk

    但当时并不懂循环流那套理论

    所以还要再学习一个(

  • 相关阅读:
    leetcode 268. Missing Number
    DBSCAN
    python二维数组初始化
    leetcode 661. Image Smoother
    leetcode 599. Minimum Index Sum of Two Lists
    Python中的sort() key含义
    leetcode 447. Number of Boomerangs
    leetcode 697. Degree of an Array
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(1月3日)
    北京Uber优步司机奖励政策(1月2日)
  • 原文地址:https://www.cnblogs.com/xryjr233/p/CF1307.html
Copyright © 2011-2022 走看看