upc 混合第十一场:魔法石

问题 D: 取模
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
数学好玩,玩好数学。
给一个正整数x,你需要回答以下两个问题:
1.找到一个正整数y∈[2,x−1],满足xmody最小。如果有多个y则求出最小的那一个。
2.找到一个正整数z∈[2,x−1],满足xmodz最大。如果有多个z则求出最小的那一个。
输入
共一行为两个正整数x,k,表示询问的数以及需要你回答的问题编号。
输出
一行一个正整数,表示对应问题的答案。
样例输入 Copy
【样例1】
4 1
【样例2】
5 2
样例输出 Copy
【样例1】
2
【样例2】
3
提示
对于40%的数据,满足3≤x≤105。
对于另外20%的数据,满足k=1。
对于另外20%的数据,满足k=2。
对于100%的数据,满足3≤x≤109,k∈{1,2}。

思路:
对于第一个操作,判断n是否是素数,如果是素数,说明区间内没有n的因子,所以nmod2是最小值;如果是素数,说明区间内存在n的因子,取模最小就是0,找到取模为0的那个数即可。
对于第二个操作,通过找规律可以发现取模最大的时候的数总是n/2 + 1

ll n;
int k;
int prime(int x)
{
	if(x == 1 || x == 0)	return 0;
	else if(x % 2 == 0 && x != 2)	return 0;
	for(int i=3;i<=sqrt(x);i+=2){
		if(x % i == 0) return 0;
	}
	return 1;
}

int main()
{
	io >> n >> k;
	if(k == 1)
	{
		if(prime(n)){ 
		// n是素数,n就只有1和n两个因子,那么nmody最小的话,y取最小值 
			printf("2\n");
		}else{ 
		// n不是素数,那么在这个区间内一定存在n的因子 
			for(int i=2;i*i<=n;i++){
				if(n % i == 0){
					printf("%d\n",i);
					return 0;
				}
			}
		}
	}
	else if(k == 2){
		printf("%d\n",n/2+1);
		return 0;
	}
}

问题 E: 宴会
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
你正在主持一场盛大的晚宴,齐聚四海宾客,共享无穷之乐。
礼堂的最前方是一张大礼桌,酒杯放置在桌上,搭成金字塔状。作为主持,你当然是亲自给这个金字塔倒酒,当所有酒杯的酒溢满之时,晚宴便走向尾声。
假设现在有n层酒杯,从上往下第i层有i个酒杯。每一个酒杯容量为1。作为一名熟练的OI选手,你可以控制每一秒钟恰好倒下1容量的酒。酒杯中的酒满了便会溢出,流到下一层相邻的两个酒杯中,如果下一层的酒杯也满了便会重复此过程。
现在你想知道,当这个过程进行了t秒之后,有多少个酒杯是满的。
如果没有理解题意的话可以看下面的图,方便理解。

输入
共一行两个正整数 n, t,表示酒杯金字塔的层数以及倒酒进行的时间。
输出
共一行,表示这个过程进行了 t 秒之后,满的酒杯的个数。
样例输入 Copy
【样例1】
3 5
【样例2】
4 8
样例输出 Copy
【样例1】
4
【样例2】
6
提示
样例解释
第一个样例中,5秒之后满的酒杯有:一二层的三个酒杯以及第三层中间的酒杯。第三层两侧的酒杯中的酒容量为1/2。

对于前10%的数据,满足t=0。
对于前20%的数据,满足n=1。
对于前40%的数据,满足n≤3。
对于另外30%的数据,满足t≥2n−1。
对于100%的数据,满足1≤n≤10,1≤t≤104。

思路:
这个题我一开始想复杂了,生生把自己给卡死
其实直接模拟就好,定义一个二维数组,a[1][1] = t,然后直接往下倒就是了,可以发现,每次倒在下一个酒杯里的数量都为上一个酒杯数量的1/2,而且上一层的酒杯会影响下面的两个酒杯,即当a[i-1][j-1] > 1的时候会影响a[i][j-1] 和 a[i][j],但是a[i][j] 同时也会被上一层的另一个酒杯影响,所以数量计算时应该是+=,倒完酒之后将a[i-1][j-1] 的值更新为1。
最后for循环寻找数量大于等于1的酒杯即可

int n,t,ans;
double a[12][12];

int main()
{
	io >> n >> t;
	
	a[1][1] = t;
	for(int i=2;i<=n;i++){
		for(int j=2;j<=i;j++){
			if(a[i-1][j-1] > 1){ // 这个酒杯满了 
				a[i][j-1] += (a[i-1][j-1]-1)*1.0/2; 
				// 对下面两个酒杯有影响,每次得到大于1部分的1/2 
				a[i][j] += (a[i-1][j-1]-1)*1.0/2;
				a[i-1][j-1] = 1.0;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			if(a[i][j] >= 1.0)	ans++;
		}
	}
	cout << ans << endl;
	return 0;
}

问题 F: 魔法石
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
你习得了魔法,并学会了熟练运用魔法石。
你得到了n颗魔法石,魔法石有两种属性,分别为火属性和水属性。你一开始得到的是这n颗魔法石的一个排列。定义这n颗魔法石释放出来的能量,为最长的属性相同的魔法石连续段的长度。
作为一名熟练的魔法师,你还可以至多修改k个魔法石的属性。你现在想知道这n颗魔法石最多可以释放出多少能量。
输入
第一行为两个正整数n,k,表示魔法石的个数和最多可以修改的魔法石数量。
接下来一行为一个长度为n的字符串,第i个字符表示第i颗魔法石的属性,a为火属性,b为水属性。
输出
输出为一行一个正整数,表示这n颗魔法石最多可以释放出的能量大小。
样例输入 Copy
【样例1】
4 2
abba
【样例2】
8 1
aabaabaa
样例输出 Copy
【样例1】
4
【样例2】
5
提示
对于30%的数据,满足n≤20。
对于60%的数据,满足n≤1000。
对于另外20%的数据,满足所有魔法石属性均相同。
对于100%的数据,满足1≤k≤n≤105。

思路:
这个题是来自一个dalao的点拨
用pos数组来实现一个前缀的思想
定义两个数组,posa和posb分别用来记录a第一次出现i个的位置和b第一次出现i个的位置,例如第一组样例,a第一次出现1个的位置是1,a第一次出现2个的位置是4,那么posa[1] = 1,posa[2] = 4
首先记录一下a和b的数量,如果当前a的数量或者b的数量小于等于k,这时候可以使用魔法改变a或者b,所以直接找一下最大的那个位置即可;如果当前a和b的数量大于k,就需要找一段序列把多余的那部分删掉,最后求最大即可
其他不明白的地方可以看注释

int n,k;
char s[maxn];
int posa[maxn],posb[maxn]; 
// posa[i] a第一次出现i个的位置 
 
int main(){
     
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
     
    int numa = 0 , numb = 0 , ans = 0;
    // numa和numb记录a和b出现的次数 
    for(int i=1;s[i];i++){
        if(s[i] == 'a') numa ++;
        else numb ++;
        if(numa <= k || numb <= k){
        	// 如果a或者b的数量满足小于等于k
			// 说明当前可以把a或者b改变一下,取最大的那个值 
            ans = max(ans , i);
        }else{
        	// 如果a和b的数量都不满足,那么就需要删掉多余的部分
			// 用当前位置减去a出现numa-k次的位置,取最大即可,numb同理 
            ans = max(ans , i - posa[numa - k]);
            ans = max(ans , i - posb[numb - k]);
        }
        if(s[i] == 'a') posa[numa] = i; // 处理一下pos数组 
        else posb[numb] = i;
    }
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你数过天上的星星吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值