“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛) 题解

“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛) 题解

萌新又来写题解啦
原题链接

B 减成一

题意:存在n个数,每次操作可以任选一个区间使得区间内的所有数字减一。问最少多少次操作,可以让所有数都变成1。

tips:洛谷原题“积木大赛”。类似的想法,连数组都不需要,只要一个last变量记录“上一个数据操作了几次”。注意如果输入的数据是1,last要置为0,表示“上一个数据不需要操作”。
答案是用差分数组做的,明显更麻烦一点~
但是可以借鉴思路!
“通过计算出差分数组,操作可以转为先选 取一个数减一再选取一个数加一(区间左端点-1,右端点后一个+1)。目标的差分数组也就变成了第一个数为1,其余为0的数组(1,1,1的差分为1,0,0)。
• 最快操作方式就是将差分数组第一个数减为1,其余减为0。即操作数为差分数组的正数之和-1。”

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int t,n;
ll num,ans,last,tmp;

void solve(){
	scanf("%d",&n);
	ans = last = 0;
	for(int i = 1; i <= n; ++i){
		scanf("%lld",&num);
		if(num == 1){
			last = 0; 
			continue;
		}
		tmp = num - 1;
		if(tmp > last)	
			ans += tmp - last;
		last = tmp;
	}
	printf("%lld\n",ans);
}
int main(){
	scanf("%d",&t);
	while(t--){
		solve();
	}
	return 0;
}

C 面积

tips:签到题。注意PI取3.14.直接算面积即可。

#include<bits/stdc++.h>
using namespace std;
const double PI = 3.14;
int t;
double x,ans;

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%lf",&x);
		ans = x * x + x * x * PI / 2.0;
		printf("%.2lf\n",ans);
	}
	return 0;
}

D 扔硬币

题意扔下了n枚硬币后,已知至少有m枚硬币是反面。请问恰好有k枚硬币是正面的概率是多少。

tips:条件概率。分两种情况:
A.对于k+m>n是不可能的,直接输出0.
B.其余情况:C(n,k)/[ 2^n - ∑(C(n,i)) ] (i<m)
套用条件概率公式。A事件为“扔n枚硬币,恰好有k枚硬币正面向上”(分子)。B事件为“扔n枚硬币,至少有m枚硬币反面向上。将至少转化为至多,即有0,1,2……(m-1)枚硬币反向的概率,用1减去即为所求。

处理完表达式后,直接用逆元+费马小定理处理数据即可。注意n 1e5的数据量,求阶乘的时候不要用(a * b ) % mod,而要使用(a % mod) * (b % mod) % mod防止溢出。

#include<bits/stdc++.h>
#define ll long long 
const ll mod = 1e9 + 7;

int n,m,k,t,fac[100005];
ll ksm(ll x ,ll n){
    ll rec = 1;
    while(n){
        if (n & 1)
            rec = (rec % mod * x % mod) % mod;
        x = (x % mod * x % mod )% mod;
        n >>= 1;
    }
    return rec;
}

void makefac(int x){
	fac[0] = 1;
	for(int i = 1; i <= x; ++i)
		fac[i] = (fac[i - 1] % mod * i % mod) % mod;
}

ll getC(int n, int m){
	return fac[n] * ksm(fac[m], mod - 2) % mod * ksm(fac[n - m], mod - 2) % mod; 
}

void solve(){
	scanf("%d%d%d",&n,&m,&k);
	if(m + k > n)	printf("0\n");
	else{
		makefac(n + 5);
		ll tmp1 = getC(n,k);
		ll tmp2 = ksm(2,n);
		ll tmp3 = 0;
		for(int i = 0; i < m; ++i)
			tmp3 = (tmp3 % mod + getC(n,i) % mod) % mod;
		ll tmp4 = (tmp2 % mod - tmp3 % mod + mod) % mod;
		printf("%lld\n",(tmp1 % mod * ksm(tmp4,mod - 2) % mod) % mod);
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		solve();
	}
	return 0;
}

E 赛马

题意:一天小明与他同学准备赛马,他们每人有n匹马,每匹马有一个固定的战力值,战力值高的马会战胜战力值低的马并赢得比赛。每匹马只能出场比赛一次。小明偷看到了他对手每匹马的出场顺序,小明在更改自己马出场顺序后最多能赢多少场比赛。

tips:与先后顺序无关~直接将两个数组排序,然后单调的过一遍自己的马即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1005;
int t,n,ans,now,bookmy[1005],book[1005];
void solve(){
	scanf("%d",&n);
	ans = 0, now = 1;
	for(int i = 1; i <= n; ++i)
		scanf("%d",&bookmy[i]);
	for(int i = 1; i <= n; ++i)
		scanf("%d",&book[i]);
	sort(bookmy + 1, bookmy + 1 + n);
	sort(book + 1, book + 1 + n);
	for(int i = 1; i <= n; ++i){
		while(bookmy[now] <= book[i] && now <= n)
			now++;
		if(now > n)	break; 
		if(bookmy[now] > book[i])	ans++,now++;
	}
	printf("%d\n",ans);
}

int main(){
	scanf("%d",&t);
	while(t--){
		solve();
	}
	return 0;
}

F.三角形

题意:小明有一根长度为a的木棒,现在小明想将木棒分为多段(每段木棒长度必须为整数),使得分隔后的木棍中,取出的任意三段都不能构成三角形,小明想知道木棒最多被分成几段?

tips: 要想取得最合适的解,三角形的边长必为fib数列。(任意相邻两项对应两短边,相加恰好等于第三边)。如20,可以分成1 1 2 3 5 8 ,共6段。剩余的长度如果不足,答案不变。(相当于把剩下的长度加到最长的边上。如22,可以分成1 1 2 3 5 10,最多只能分6段)。

要注意a的范围 2^64 - 1,开ll会爆掉(2 ^ 63),所以要用unsigned long long ,读入时使用%llu占位符!

#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
 
ll a,ans,now,last,rec;
int t;
 
void solve(){
    scanf("%llu",&a);
    now = 2, last = 1, ans = 2;
    if(a == 1){
        printf("1\n");
        return ;
    }  
    if(a == 2 || a == 3)    printf("2\n");
    else{
        a -= 2;
        while(a >= now){
            a -= now;
            ans++;
            rec = now;
            now += last;
            last = rec;
        }
        printf("%llu\n",ans);
    }
}
 
int main(){
    scanf("%d",&t);
    while(t--){
        solve();
    }
    return 0;
}

H.直线

题意:平面上存在n条直线。请问n条直线在平面上最多存在多少交点。

tips:n条直线两两相交,最多有n(n-1) / 2个交点。 n的范围达到1e15,要用大数计算。

import java.math.BigInteger;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int t = cin.nextInt();
		while(t-- > 0) {
			BigInteger n = cin.nextBigInteger();
			BigInteger tmp = n.multiply(n.subtract(BigInteger.valueOf(1)));
			System.out.println(tmp.divide(BigInteger.valueOf(2)));
		}
	}
}

J 最大值

题意:有一个字符串s,对于字符串中一个非前缀子串恰好为字符串的前缀我们称之为ac串。请问给出一个字符串他的ac串最大长度为多少。

tips:AC串其实是kmp中next数组的含义,所以求出字符串的next数组即可得到答案。(我这里求的是D数组,遍历D数组,最大值即为答案。)

#include<bits/stdc++.h>
using namespace std;

int t,ans;
char str[100005];  
int d[100005];
void Getd(const char *p,int d[] ){
    int i = 1, j = 0, np = strlen(p);
    while(i < np){
        if(p[j] == p[i])
            d[i++] = ++j;
        else{
            if(j > 0)   j = d[j-1];
            else        i++;
        }
    }
}

int main(){
	scanf("%d",&t);
	while(t--){
		memset(d,0,sizeof(d));
		getchar();
		scanf("%s",str);
		Getd(str, d);
		int len = strlen(str);
		ans = 0;
		for(int i = 0; i < len; ++i){
			int tmp = d[i];
			ans = max(ans,tmp);
		}
		printf("%d\n",ans);
	} 
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值