2019牛客暑期多校训练营 | 第七场解题报告(A、C)

A-string

给出一段01构成的字符串,要求把字符串尽可能切分成少的份数,使得每一段字符串都是在其循环移位的所有字符串中字典序最小的那一个。

一开始只想着找规律,结果发现找到的规律都是错的。其实我们可以先从题意入手,要保证一个字符串的循环字典序最小,那么从字符串中每一位作为开头的循环串的字典序都不能比原字符串大。因此可以从头确定一个位置,然后从截取最长的字符串开始以此判断截取的字符串是否符合要求,如果不符合减少截取的长度,如果符合就截取该字符串,并从截取的位置下一位继续以上操作,直到所有字符串都被截取。

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 210;

int vis[maxn];
char s[maxn];
int d[maxn];

int mystrcmp(int h1,int h2,int t2){
	int flag=1;
	for(int i=h1,j=h2; ; i++, j++){
		if( s[j] > s[i] ){
			break;
		}
		if( s[j] < s[i] ){
			flag=0;
			break;
		}
		if( j==t2 ){
			j=h1-1;//跳转到开头位置
		}
		if( i==t2 ){
			break;
		}
	}
	return flag;
} 

int main(void){
	int t;
	int p;
	int h1,h2;
	int t1,t2;
	int k;
	scanf("%d",&t);
	while( t-- ){
		memset(vis,0,sizeof(vis));
		memset(vis,0,sizeof(d));		
		scanf("%s",s);
		int len = strlen(s);
		for( p=0; p<len; p++){
			if( s[p]=='0' ){
				if( p>0 ) vis[p-1]=1;
				h1=p;
				break;
			}
		}
		for( p=len-1; p>=0; p--){
			if( s[p]=='1' ){
				if( p<len-1 ) vis[p]=1;
				break;
			}			
		}
		t1=p;
		k=0;
		for(int i=h1; i<=t1; i++){
			if( s[i]=='0' && ( i==0 || s[i-1]=='1' )){
				d[k++]=i;
			}
		}
		d[k]=t1+1;
		int t2k=k;
		while( h1<t1 ){
			t2=d[t2k]-1;		
			for(int tk=t2k-1; ; tk--){
				h2=d[tk];
				if( h2==h1 ){
					vis[t2]=1;
					h1=t2+1;
					t2k=k;
					break;
				}				
				if( mystrcmp(h1,h2,t2)==0 ){
					t2k--;
					break;
				}

			}
		}
		for(int i=0; i<len; i++){
			printf("%c",s[i]);
			if( vis[i] ) printf(" ");
		}
		printf("\n");
	}

	return 0;
}

C-Governing sand

题意现有n种树,并给出每种树的高度H[i],砍掉一课的花费C[i],以及树的数量P[i],求最小的代价砍去一部分树,让最高的树的数量超过所有树的一半。

思路是从低到高枚举树的高度作为最高的树,并统计当前高度树的数量m,然后把选定高度之上的树全部砍倒,然后再从高度之下的树中砍去代价最小的部分树使剩余树的数量小于等于2m-1。

问题关键在于如何选取高度之下应该砍去树的范围,解决方法有很多,但是比较好的方法是用建立一个按照花费从小到大建立的权值线段树,统计每个花费对应树的数目,这样就可以便于查询从一堆树中砍掉任意棵树的最小代价。

#include<cstdio>
#include<algorithm>
using namespace std;
const int max_cost=210;
const int maxn=100010;
typedef long long LL;

struct node{
	int l,r;
	LL num;
	LL cost;
}tree[max_cost*3];

struct data{
	LL h,c;
	LL p; 
}a[maxn];

bool cmp(data a,data b){
	return a.h<b.h;
}

void build(int id,int l,int r){
	tree[id].l=l;
	tree[id].r=r;
	tree[id].num=0;
	tree[id].cost=0;
	if( l==r ){
		return;
	}
	int mid = (l+r)>>1;
	build( id<<1, l, mid);
	build( id<<1|1, mid+1, r);

}

LL query(int id, LL k){
	if( k<=0 )return 0;
	
	if( tree[id].num<=k ){
		return tree[id].cost;
	}
	if( tree[id].l==tree[id].r){
		return k*tree[id].l; 
	}
	
	if( tree[id<<1].num>=k ){
		return query( id<<1,k);	
	}
	else{
		return tree[id<<1].cost + query( id<<1|1,k-tree[id<<1].num);//从右子树总找剩下需要的 
	}
	
	
} 

void updata(int id, LL c, LL p){
	if( tree[id].l==tree[id].r ){
		tree[id].num+=p;
		tree[id].cost=tree[id].num*tree[id].l;
		return;
	}
	if( c <= tree[id<<1].r ) updata( id<<1, c, p);
	else updata( id<<1|1, c, p);
	tree[id].num = tree[id<<1].num + tree[id<<1|1].num;
	tree[id].cost = tree[id<<1].cost + tree[id<<1|1].cost;
}

int main(void){
	int n;
	while( ~scanf("%d",&n) && n ){
		LL sum=0;
		LL ans;
		LL cost;
		LL num;
		build(1,1,200);		
		for(int i=0; i<n; i++){
			scanf("%lld%lld%lld",&a[i].h,&a[i].c,&a[i].p);
			sum += a[i].c * a[i].p;
		}
		sort(a,a+n,cmp);	
		ans = sum;
		LL fnum=0;//当前高度之下的树的数量		
		for(int i=0,j=0; i<n; ){
			num=0;
			j=i;
			while( j<n && a[j].h==a[i].h ){
				sum -= a[j].c*a[j].p;
				num += a[j].p;
				j++;
			}//将种类不同高度相同的树归到一起 
			cost = sum;//砍掉比当前高度高的所有树的花费 
			cost += query(1, fnum-(num-1) );//砍到留下num-1棵树需要的花费 
			ans=min(ans,cost);
			
			j=i;
			while( j<n && a[j].h==a[i].h){
				updata(1, a[j].c, a[j].p);
				j++;
			}
			i=j;
			fnum+=num;
		}
		printf("%lld\n",ans);
	
	}	

	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值