Codeforces Round #581 (Div. 2) A(规律),B(简单模拟),C(Floyd判断最短路),D(规律+线段树)

Contest:http://codeforces.com/contest/1204

A. BowWow and the Timetable(规律)

题目链接:http://codeforces.com/contest/1204/problem/A

题目大意:火车的开出时间是4^k。给出出发时间(二进制数),判断错过了多少趟火车。

思路:很容易发现,由于是一个合法的二进制数,因此第一位必定为1,那么我们判断有多少位就好了。两位一个4,相当于len/2个列车,如果是奇数&&后面有1的话,说明仍然错过了最近的一站。

ACCode:

int main(){
	string s;
	while(cin>>s){
		int len=s.size();
		int ans=0,flag=0;
		if(len&1){//len奇数 
			ans=len/2;
			for(int i=len-1;i>0;--i){
				if(s[i]=='1') flag=1;
			}
			if(flag) ++ans;
		}
		else{
			ans=len/2;
		}cout<<ans<<endl;
	}
	
}

B. Mislove Has Lost an Array(简单模拟)

题目链接:http://codeforces.com/contest/1204/problem/B

题目大意:给出数组长度,种类数的下界和上界。求数组元素的最大和,和最小和。数组中元素只能是2^k,且必须存在2^(k-1)。

思路:最小值,满足下届的前面全是1.最大值,满足上界的后面全是最大值。

ACCode:

int n,l,r;
 
int main(){
	while(~scanf("%d%d%d",&n,&l,&r)){
		ll sum1=0,sum2=0,cnt=0;
		for(int i=1;i<=l;++i){
			sum1+=(1ll<<(i-1));
		}sum1+=n-l;
//		printf("sum1=%lld\n",sum1);
		for(int i=1;i<=r;++i){
			sum2+=(1ll<<(i-1));
//			printf("sum=%lld\n",sum2);
		}
		for(int i=1;i<=n-r;++i){
			sum2+=(1ll<<(r-1));
//			printf("%lld\n",sum2);
		}printf("%lld %lld\n",sum1,sum2);
	} 
	
}

C. Anna, Svyatoslav and Maps(Floyd判断最短路)

题目链接:http://codeforces.com/contest/1204/problem/C

题目大意:给出一个n个点的有向图。给你一条路径,输出缩小之后的路径。路径可以被缩小a->b->c,缩小a->c只能是b只连接a和c。

思路:跑一边Floyd,就可以得到所有联通的点和最短路了。对于每个点,如果之间的距离就是最短路,那么就可以直接相连,否者中间必定存在其他的最短路径。两个同样的元素不可相邻:比如a->b->a不可缩减成a->a。特判一下就好了。

ACCode:

char S[MAXN];
int MP[MAXN][MAXN],Cnt[MAXN][MAXN];
int A[MAXN*MAXN*MAXN];
int Ans[MAXN*MAXN*MAXN],tot;
int n;
 
void Floyd(){
	for(int k=1;k<=n;++k){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				if(MP[i][j]>MP[i][k]+MP[k][j]){
					MP[i][j]=MP[i][k]+MP[k][j];
					Cnt[i][j]=1;
				}
				else if(MP[i][j]==MP[i][k]+MP[k][j]){
					Cnt[i][j]++;
				}
			}
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%s",&S);
//		printf("S=%s\n",S); 
		for(int j=1;j<=n;++j){
			if(S[j-1]=='1') MP[i][j]=1;
			else MP[i][j]=INF32;
		}
	}Floyd();
//	for(int i=1;i<=n;++i){
//		for(int j=1;j<=n;++j){
//			printf("%d ",MP[i][j]);
//		}printf("\n");
//	}
//	for(int i=1;i<=n;++i){
//		for(int j=1;j<=n;++j){
//			printf("%d ",Cnt[i][j]);
//		}printf("\n");
//	}
	int m;scanf("%d",&m);
	for(int i=1;i<=m;++i){
		scanf("%d",&A[i]);
	}
	tot=0;
	for(int i=1,j;i<=m;i=j){
		j=i+2;
//		printf("judge: i=%d j=%d MP[%d][%d]=%d\n",i,j,A[i],A[j],MP[A[i]][A[j]]);
		while(j<=m&&j-i<=MP[A[i]][A[j]]) ++j;--j;//[i,j]中都可以省略[i,j+1]不可省略 
		if(A[i]==A[j]) --j;
		Ans[++tot]=A[i];
		//if(j==m&&i<m) Ans[++tot]=A[j];//一直到最后 
//		printf("end: i=%d j=%d\n",i,j);
	}
	printf("%d\n%d",tot,Ans[1]);
	for(int i=2;i<=tot;++i) printf(" %d",Ans[i]);printf("\n");
	
}

D2. Kirk and a Binary String (hard version)(规律+线段树)

题目链接:http://codeforces.com/contest/1204/problem/D2

题目大意:给出一个01串S,让你求出另一个串P满足长度相等。所有区间的不减子序列长度相等(注意是子序列,不是子串).

思路:经过观察我们发现,可将01串的[1,x]的最长不减子序列长度求出:

然后发现,每个0的最多上升空间,然后我们就可以判断能否将1修改为0,很明显,将1修改为0后,影响的是后面的0.所以用线段树维护一下最多的修改次数就好了。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
      
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
 
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
 
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
// freopen(".in","r",stdin);freopen(".out","w",stdout);
 
struct SegTree{
	struct Node{
		int l,r;
		int mxadd;
		//可达到的最大值	当前值	可增加多少 
		int lazy;
	};
	Node Tree[MAXN<<2];
	
	void PushUp(int rt){
		Tree[rt].mxadd=min(Tree[rt<<1].mxadd,Tree[rt<<1|1].mxadd);
	}
	void PushDown(int rt){
		if(Tree[rt].lazy){
			Tree[rt<<1].lazy=Tree[rt<<1|1].lazy=Tree[rt].lazy;
			Tree[rt<<1].mxadd+=Tree[rt].lazy;
			Tree[rt<<1|1].mxadd+=Tree[rt].lazy;
			Tree[rt].lazy=0;
		}
	}
	void Build(int l,int r,int rt,int A[]){
		if(l>r) return ;
		Tree[rt].l=l;Tree[rt].r=r;
		Tree[rt].lazy=0;
		if(l==r){
			Tree[rt].mxadd=A[l];
			return ;
		}int mid=(l+r)>>1;
		Build(l,mid,rt<<1,A);Build(mid+1,r,rt<<1|1,A); 
		PushUp(rt);
	}
	void Update(int ql,int qr,int val,int rt){//区间修改+1 
		if(ql>qr) return ;
		if(ql<=Tree[rt].l&&Tree[rt].r<=qr){
			Tree[rt].mxadd+=val;
			Tree[rt].lazy+=val;
			return ;
		}PushDown(rt);
		if(qr<=Tree[rt<<1].r) Update(ql,qr,val,rt<<1);
		else if(ql>=Tree[rt<<1|1].l) Update(ql,qr,val,rt<<1|1);
		else{
			Update(ql,qr,val,rt<<1);
			Update(ql,qr,val,rt<<1|1);
		}PushUp(rt);
	}
	void Show(int rt){
		printf("l=%d r=%d mxadd=%d lazy=%d\n",Tree[rt].l,Tree[rt].r,Tree[rt].mxadd,Tree[rt].lazy);
		if(Tree[rt].l==Tree[rt].r) return ;
		Show(rt<<1);Show(rt<<1|1);
	}
};
SegTree Seg;
char S[MAXN];
int A[MAXN],B[MAXN],C[MAXN],Ans[MAXN];
 
int main(){
//	freopen("D.in","r",stdin);freopen("D.out","w",stdout);
	while(~scanf("%s",&S)){
		int len=strlen(S);
		int o=0,z=0;
		for(int i=0,mx=0;i<len;++i){
			if(S[i]=='1'){ ++o; A[i]=++mx; }
			else{ A[i]=++z; mx=max(mx,A[i]); }
		}
//		for(int i=0;i<len;++i) printf("%c ",S[i]);printf("\n");
//		for(int i=0;i<len;++i) printf("%d ",A[i]);printf("\n");
		for(int i=1;i<=len;++i) B[i]=max(B[i-1],A[i-1]);
//		for(int i=1;i<=len;++i) printf("%d ",B[i]);printf("\n");
		for(int i=1,j=0;i<=len;++i){//最大可上升多少 
			if(S[i-1]=='0'){
				C[++j]=B[i]-A[i-1];
			}
		}
//		for(int i=1;i<=z;++i) printf("%d ",C[i]);printf("\n"); 
		Seg.Build(1,z,1,C);
//		Seg.Show(1);printf("\n");
		for(int i=1,j=1;i<=len;++i){
			//后面修改的0为[j,z] 
//			printf("i=%d j=%d\n",i,j);
			if(S[i-1]=='1'){//判断是否可以缩减为0 
				Seg.Update(j,z,-1,1);
				Seg.Update(1,j-1,100,1);//排除前面的影响 
				if(Seg.Tree[1].mxadd<0){
					Seg.Update(j,z,1,1);
					Ans[i]=1;
//					printf("NotChange\n");
				}
				else Ans[i]=0;
			}
			else{
				Ans[i]=0;
				++j;
			}
//			Seg.Show(1);printf("\n");
		}
		for(int i=1;i<=len;++i) printf("%d",Ans[i]);printf("\n");
	}
	
}
//0111001100111011101000
//0011001100001000000000
//0011001100001011101000

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值