金山西山居初赛第三场

http://acm.hdu.edu.cn/showproblem.php?pid=4551

hdu4551 生日猜猜猜

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 224    Accepted Submission(s): 91

Problem Description
小明对生日十分看重,因为他可以得到祝福,可以和朋友亲人一起分享快乐,可以为自己的人生做一次总结,并且...能够收到好多礼物!
不过小明是个神秘的人,不会轻易告诉你他的生日,现在他想到一个办法,让你去猜他的生日是哪一天。

小明会告诉你如下三个信息:

1. 出生月份和出生日子的最大公约数;
2. 出生月份和出生日子的最小公倍数;
3. 出生年份;

现在要求你猜出小明的生日。
 

Input
第一行输入一个正整数T,表示总共有T组册数数据(T <= 200);
对于每组数据依次输入三个数x,y,z,
x表示出生月份和出生日子的最大公约数(1<= x <=1000);
y表示出生月份和出生日子的最小公倍数(1<= y <=1000);
z表示出生年份(1900 <= z <= 2013)。
每组输入数据占一行。
 

Output
对于每组数据,先输出Case数。
如果答案不存在 ,输出“-1”;
如果答案存在但不唯一 ,输出“1”;
如果答案唯一,输出生日,日期格式为YYYY/MM/DD;
每组输出占一行,具体输出格式参见样例。
 

Sample Input
  
  
3 12 24 1992 3 70 1999 9 18 1999

#include<cstdio>
#include<cstring>
int A[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
bool J(int y){return y%400==0||(y%4==0&&y%100!=0);}
int main(){
    int T,ca=1,x,y,z,a,b,c,i,j;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&x,&y,&z);
        A[2]=28+J(z);c=0;
        for(i=1;i<=12&&c<2;i++)for(j=1;j<=A[i]&&c<2;j++){
            if(gcd(i,j)==x&&lcm(i,j)==y)a=i,b=j,c++;
        }
        printf("Case #%d: ",ca++);
        if(c==0)puts("-1");
        else if(c>1)puts("1");
        else printf("%d/%02d/%02d\n",z,a,b);
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=4552

hdu4552 怪盗基德的挑战书

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 145    Accepted Submission(s): 60

Problem Description
  “在树最美丽的那天,当时间老人再次把大钟平均分开时,我会降临在灯火之城的金字塔前,带走那最珍贵的笑容。”这是怪盗基德盗取巴黎卢浮宫的《蒙娜丽莎的微笑》这幅画时,挑战书上的内容。
  但这次,怪盗基德的挑战书上出现了一串串小写字母“aaab sdfeeddd...”。柯南以小学生的眼睛,超凡高中生的头脑,快速统计各种字母频率,字符串长度,并结合挑战书出现的时间等信息,试图分析怪盗基德的意图。最后,他将线索锁定在字符串的循环次数上。并且进一步推理发现,从字符串的第一位开始,到第i位,形成该字符串的子串(c1, c2, c3 ... ci )。对于某一子串ci在该字符串中出现的次数记为ki,则全部子串的循环次数总和AIM = k1 + k2 + ... + ki + ... + kn,柯南发现,AIM恰好对应一个ASCII码!所以,只要把挑战书上的字符串转变成数字,再找到对应的ASCII码,就可以破解这份挑战书了!
  现在,你的任务就是把字符串转变成对应数字,因为ASCII码以及扩展ASCII码全部只有256个,所以,本题只要把结果对256取余即可。
 

Input
输入有多组测试数据;
每组测试数据只有一个字符串,由各种小写字母组成,中间无空格。
字符串的长度为L(0 < L <= 100000)。
 

Output
请计算并输出字符串的AIM值,每组数据输出一行。
 

Sample Input
  
  
aaa abab
 

Sample Output
  
  
6 6
 


分析:这题的数据不是一般的“水”,暴力O(n^2)也能过,而且竟然跑得不比正解慢,比较奇葩,下面是暴力代码,据说后缀数组可解,思想是一样的,求出所有后缀与原串的最长公共前缀长度

#include<cstdio>
char s[100000];
int main(){
    int a,i,j,k;
    while(gets(s)){
        for(a=i=0;s[i];i++){
            for(j=0,k=i;s[k]&&s[k]==s[j];j++,k++);
            a+=j;
        }
        printf("%d\n",a%256);
    }
    return 0;
}


后缀数组:

#include<cstdio>
#include<cstring>
#define SIZE 100002
char S[SIZE]; // SIZE > 256
int N,sa[SIZE],height[SIZE],rank[SIZE],tmp[SIZE],top[SIZE];
//S[] 字符串
//N 字符串长度
//rank[i] i是第几名 
//sa[i] 第i名是什么
//height[i] 第i名和第i-1名的最长公共前缀长度
//名次:1~N  序号:0~N-1
void makesa(){ // O(SIZE * log SIZE)
	int i,j,n,len,na;
	n=N+1;
	S[n-1]=0;
	na=(n<256?256:n);
	memset(top,0,na*sizeof(int));
	for(i=0;i<n;i++)top[rank[i]=S[i]&0xff]++;
	for(i=1;i<na;i++)top[i]+=top[i-1];
	for(i=0;i<n;i++)sa[--top[rank[i]]]=i;
	for(len=1;len<n;len<<=1){
		for(i=0;i<n;i++){
			j=sa[i]-len;if(j<0)j+=n;
			tmp[top[rank[j]]++]=j;
		}
		sa[tmp[top[0]=0]]=j=0;
		for(i=1;i<n;i++){
			if(rank[tmp[i]]!=rank[tmp[i-1]]||
				rank[tmp[i]+len]!=rank[tmp[i-1]+len])
				top[++j]=i;
			sa[tmp[i]]=j;
		}
		memcpy(rank,sa,n*sizeof(int));
		memcpy(sa,tmp,n*sizeof(int));
		if(j>=n-1)break;
	}
}
void lcp(){ // O(4 * SIZE)
	int i,j,k,n=N+1;
	for(j=rank[height[i=k=0]=0];i<n-1;i++,k++)
		while(k>=0&&S[i]!=S[sa[j-1]+k])
			height[j]=(k--),j=rank[sa[j]+1];
}
int main(){
	while(gets(S)){
		N=strlen(S);
		makesa();
		lcp();
		int ans=N,i,p=rank[0],mi=SIZE;
		for(i=p;i>1;i--){
			if(mi>height[i])mi=height[i];
			if(mi==0)break;
			ans+=mi;
		}mi=SIZE;
		for(i=p+1;i<=N;i++){
			if(mi>height[i])mi=height[i];
			if(mi==0)break;
			ans+=mi;
		}
		printf("%d\n",ans%256);
	}
	return 0;
}

KMP 跟暴力差不多的复杂度

#include<cstdio>
#include<cstring>
const int N=100002;
char S[N];
int p[N],la;
void get_next(){
	la=strlen(S+1);
	int i,j;
	for(j=p[1]=0,i=2;i<=la;i++){
		while(j&&S[j+1]!=S[i])j=p[j];
		if(S[j+1]==S[i])++j;
		p[i]=j;
	}
}
int main(){
	while(gets(S+1)){
		get_next();
		int ans=la,i,j;
		for(i=j=1;i<=la;i++,j=i)while(p[j])ans++,j=p[j];
		printf("%d\n",ans%256);
	}
	return 0;
}


记忆化优化,复杂度降为O(n)

#include<cstdio>
#include<cstring>
const int N=100002;
char S[N];
int p[N],la,c[N];
void get_next(){
	la=strlen(S+1);
	int i,j;
	for(j=p[1]=0,i=2;i<=la;i++){
		while(j&&S[j+1]!=S[i])j=p[j];
		if(S[j+1]==S[i])++j;
		p[i]=j;
	}
}
int cal(int x){
	if(p[x])return c[p[x]]+1;
	return 0;
}
int main(){
	while(gets(S+1)){
		get_next();
		int ans=la,i,j;
		for(i=1;i<=la;i++)ans+=c[i]=cal(i);
		printf("%d\n",ans%256);
	}
	return 0;
}


 

http://acm.hdu.edu.cn/showproblem.php?pid=4553

hdu4553 约会安排

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 80    Accepted Submission(s): 25


Problem Description
  寒假来了,又到了小明和女神们约会的季节。
  小明虽为屌丝级码农,但非常活跃,女神们常常在小明网上的大段发言后热情回复“呵呵”,所以,小明的最爱就是和女神们约会。与此同时,也有很多基友找他开黑,由于数量实在过于巨大,怎么安排时间便成了小明的一大心事。
  我们已知小明一共有T的空闲时间,期间会有很多女神或者基友来找小明。
  作为一个操作系统曾经怒考71分的大神,小明想到了一个算法,即“首次适应算法”,根据操作系统课本的描述,就是找一段最靠前的符合要求的连续空间分配给每个请求,由此小明做出了一个决定:
  当一个基友来找小明时,小明就根据“首次适应算法”来找一段空闲的时间来和基友约好,如果找到,就说“X,let’s fly”(此处,X为开始时间),否则就说“fly with yourself”;
  当女神来找小明时,先使用一次“首次适应算法”,如果没有找到,小明就冒着木叽叽的风险无视所有屌丝基友的约定,再次使用“无视基友首次适应算法”,两次只要有一次找到,就说“X,don’t put my gezi”(此处,X为开始时间),否则就说“wait for me”
  当然,我们知道小明不是一个节操负无穷的人,如果和女神约会完,还有剩余时间,他还是会和原来约好的基友去dota的。(举个例子:小西(屌丝)和小明约好在1~5这个时间单位段内打dota,这时候,女神来和小明预约长度为3的时间段,那么最终就是1~3小明去和女神约会,搞定后在4~5和小西打dota)
  小明偶尔也会想要学习新知识,此时小明就会把某一个时间区间的所有已经预定的时间全部清空用来学习并且怒吼“I am the hope of chinese chengxuyuan!!”,不过小明一般都是三分钟热度,再有人来预定的话,小明就会按耐不住寂寞把学习新知识的时间分配出去。
 

Input
输入第一行为CASE,表示有CASE组测试数据;
每组数据以两个整数T,N开始,T代表总共的时间,N表示预约请求的个数;
接着的N行,每行表示一个女神或者基友的预约,“NS QT”代表一个女神来找小明约一段长为QT的时间,“DS QT”则代表一个屌丝的长为QT的请求,当然也有可能是小明想学知识了,“STUDY!! L R”代表清空L~R区间内的所有请求。

[Technical Specification]
1. 1 <= CASE <= 30
2. 1 <= T, N <= 100000
3. 1 <= QT <= 110000
4. 1 <= L <= R <=T
 

Output
对于每一个case,第一行先输出“Case C:”代表是第几个case,然后N行,每行对应一个请求的结果(参照描述)。
输出样本(可复制此处):
“X,let's fly”,”fly with yourself”,”X,don't put my gezi”,”wait for me”,”I am the hope of chinese chengxuyuan!!”
 

Sample Input
  
  
1 5 6 DS 3 NS 2 NS 4 STUDY!! 1 5 DS 4 NS 2
 

Sample Output
  
  
Case 1: 1,let's fly 4,don't put my gezi wait for me I am the hope of chinese chengxuyuan!! 1,let's fly 1,don't put my gezi


分析:尽管一看就知道是线段树,但身为菜鸟的我不知如何解,下面贴下网上代码(写得很清楚,膜拜一下!)

#include<cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int max(int a,int b){return a>b?a:b;}
const int N=100002;
int conl[N<<2][2],conr[N<<2][2],mx[N<<2][2],col[N<<2];
void pushup(int l,int m,int r,int rt){
	for(int i=0;i<2;i++){
		if(conl[rt<<1][i]==m-l+1){
			conl[rt][i]=conl[rt<<1][i]+conl[rt<<1|1][i];
		}else conl[rt][i]=conl[rt<<1][i];
		if(conr[rt<<1|1][i]==r-m){
			conr[rt][i]=conr[rt<<1][i]+conr[rt<<1|1][i];
		}else conr[rt][i]=conr[rt<<1|1][i];
		mx[rt][i]=max(conr[rt<<1][i]+conl[rt<<1|1][i],max(mx[rt<<1][i],mx[rt<<1|1][i]));
	}
}
void pushdown(int l,int m,int r,int rt){
	if(col[rt]!=-1){
		if(col[rt]==0){
			conl[rt<<1][0]=conr[rt<<1][0]=mx[rt<<1][0]=m-l+1;
			conl[rt<<1][1]=conr[rt<<1][1]=mx[rt<<1][1]=m-l+1;
			conl[rt<<1|1][0]=conr[rt<<1|1][0]=mx[rt<<1|1][0]=r-m;
			conl[rt<<1|1][1]=conr[rt<<1|1][1]=mx[rt<<1|1][1]=r-m;
		}else if(col[rt]==1){
			conl[rt<<1][0]=conr[rt<<1][0]=mx[rt<<1][0]=0;
			conl[rt<<1][1]=conr[rt<<1][1]=mx[rt<<1][1]=m-l+1;
			conl[rt<<1|1][0]=conr[rt<<1|1][0]=mx[rt<<1|1][0]=0;
			conl[rt<<1|1][1]=conr[rt<<1|1][1]=mx[rt<<1|1][1]=r-m;
		}else {
			conl[rt<<1][0]=conr[rt<<1][0]=mx[rt<<1][0]=0;
			conl[rt<<1][1]=conr[rt<<1][1]=mx[rt<<1][1]=0;
			conl[rt<<1|1][0]=conr[rt<<1|1][0]=mx[rt<<1|1][0]=0;
			conl[rt<<1|1][1]=conr[rt<<1|1][1]=mx[rt<<1|1][1]=0;
		}
		col[rt<<1]=col[rt<<1|1]=col[rt];
		col[rt]=-1;
	}
}
void build(int l,int r,int rt){
	mx[rt][0]=conl[rt][0]=conr[rt][0]=r-l+1;
	mx[rt][1]=conl[rt][1]=conr[rt][1]=r-l+1;
	col[rt]=-1;
	if(l==r)return;
	int m=(l+r)>>1;
	build(lson);
	build(rson);
	pushup(l,m,r,rt);
}
void update(int L,int R,int val,int l,int r,int rt){
	if(L<=l&&r<=R){
		if(val==0){
			mx[rt][0]=conl[rt][0]=conr[rt][0]=r-l+1;
			mx[rt][1]=conl[rt][1]=conr[rt][1]=r-l+1;
		}else if(val==1){
			mx[rt][0]=conl[rt][0]=conr[rt][0]=0;
			mx[rt][1]=conl[rt][1]=conr[rt][1]=r-l+1;
		}else {
			mx[rt][0]=conl[rt][0]=conr[rt][0]=0;
			mx[rt][1]=conl[rt][1]=conr[rt][1]=0;
		}
		col[rt]=val;
		return;
	}
	int m=(l+r)>>1;
	pushdown(l,m,r,rt);
	if(L<=m)update(L,R,val,lson);
	if(m<R)update(L,R,val,rson);
	pushup(l,m,r,rt);
}
int query(int k,int len,int l,int r,int rt){
	if(mx[rt][k]<len)return -1;
	int m=(l+r)>>1;
	pushdown(l,m,r,rt);
	if(mx[rt<<1][k]>=len)return query(k,len,lson);
	else if(conr[rt<<1][k]+conl[rt<<1|1][k]>=len)return m-conr[rt<<1][k]+1;
	else if(mx[rt<<1|1][k]>=len)return query(k,len,rson);
}
int main(){
	int T,ca=1,len,n,m,x,a,b;
	char s[10];
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		printf("Case %d:\n",ca++);
		build(1,n,1);
		while(m--){
			scanf("%s",s);
			if(s[0]=='D'){
				scanf("%d",&len);
				x=query(0,len,1,n,1);
				if(x==-1){puts("fly with yourself");continue;}
				printf("%d,let's fly\n",x);
				update(x,x+len-1,1,1,n,1);
			}else if(s[0]=='N'){
				scanf("%d",&len);
				x=query(0,len,1,n,1);
				if(x==-1){
					x=query(1,len,1,n,1);
					if(x==-1){puts("wait for me");continue;}
				}
				printf("%d,don't put my gezi\n",x);
				update(x,x+len-1,2,1,n,1);
			}else{
				scanf("%d%d",&a,&b);
				puts("I am the hope of chinese chengxuyuan!!");
				update(a,b,0,1,n,1);
			}
		}
	}
	return 0;
}


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值