【题目记录】——2021“MINIEYE杯”中国大学生算法设计超级联赛(7)


题目集地址 2021“MINIEYE杯”中国大学生算法设计超级联赛(7)
题目集原地址 2021“MINIEYE杯”中国大学生算法设计超级联赛(7)
这次题目集我们做了3道题目,1003,1010,1012

1003 Fall with Trees 数学几何

题目地址1003 Fall with Trees
思路:
这道题基本上就是找规律然后求公式,我们可以设树层与层之间的高为h,根节点为第一层的话,第二层两个结点间的距离的一半为a,则第二层是2a,第三层是3a,第五层是(7/2)a,可以推出每一层的长度的通项公式为:(4-(1/2)k)*a,其中k=n-2,n为层数。
这样推公式容易理解,但是需要对只有两层的情况进行特判。其实我们可以发现面积就是三角形加上几个梯形的面积。因此写出梯形面积公式后求和即可,涉及到等比数列求和的部分直接使用求和公式即可。
AC代码:

#include <iostream>
#include <bits/stdc++.h>

#define INF 0x3f3f3f3f
#define pi 3.141592653589793238462643383279
#define eps 1e-6

using namespace std;
typedef long long ll;
double a[10010];
void init()//初始化a数组,里面存的是2的次幂
{
    a[0]=1;
    for(int i=1;i<=10001;i++)
    {
        a[i]=a[i-1]*0.5;
    }
}
void solve()
{
    int k,kk;
    scanf("%d",&k);
    double x[10],y[10];
    for(int i=0;i<3;i++)
    {
        scanf("%lf%lf",&x[i],&y[i]);
    }
    double h=abs(y[1]-y[0]);
    double cha=abs(x[1]-x[0]);
    double res;
    res=2*cha*h;
    if(k==2)
    {
        res/=2;
        printf("%.3lf\n",res);
        return ;
    }
    kk=k-2;
    double res1=4*(kk-1)+a[kk-1]+a[kk];
    res+=(res1*h*cha);
    printf("%.3lf\n",res);
}

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

1004 Link with Balls 排列组合+数学

题目地址Link with Balls
题意:有n个框,在第2x-1个框中可以取kx个球(k为整数),在第2x个框中最多可以取x个球。要取m个球,有多少种方案。
思路:将可以取0~k-1个球的框与只能取k的倍数个球的框合并为一个可以取任意个球的框,就得到了 n个可以取任意个球的框和一个可以取0~n个球的框。枚举0~n个球的框中取出了多少个球,剩下的球的选取方案可以由插板法得到,于是答案为 ∑ i = 0 n C m − i + n − 1 n − 1 \sum_{i=0}^{n}C_{m-i+n-1}^{n-1} i=0nCmi+n1n1,即 C m + n n − C m − 1 n C_{m+n}^{n}-C_{m-1}^n Cm+nnCm1n

公式是由数学推导得来的,不是很懂,但是求组合数的板子还是很好用的。

#include <stdio.h>
#define MN 2000000
typedef long long ll;
const int mod = 1000000007;

int fac[MN+5],inv[MN+5];

ll qpow(ll bsc,ll y){
	ll ret = 1;
	while(y){
		if(y&1) ret = ret*bsc%mod;
		bsc = bsc*bsc%mod;
		y >>= 1; 
	}
	return ret;
}

void init(){
	fac[0] = 1;
	for(int i=1;i<=MN;i++)
		fac[i] = (ll)fac[i-1]*i%mod;
	inv[MN] = qpow(fac[MN],mod-2);
	for(int i=MN-1;i>=0;i--)
		inv[i] = (ll)inv[i+1]*(i+1)%mod;
}

int C(int n,int m){
	if(m>n) return 0;
	return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
}

void solve(){
	int n,m;
	scanf("%d%d",&n,&m);
	int ans = C(m+n,n)-C(m-1,n);
	if(ans<0) ans += mod;
	printf("%d\n",ans);
}

int main(){
	init();
	int T;
	scanf("%d",&T);
	while(T--) solve();
}

1008 Smzzl with Greedy Snake 模拟

题目地址1008 Smzzl with Greedy Snake
题意:一个贪吃蛇,有初始位置和方向,吃到一个点下一个点才会出现,给定几个点,输出最短的操作序列,操作c顺时针转,操作u逆时针转,操作f前进,不考虑咬到自身的情况。
思路:这个题是我最后做的,但是当时没做出来,想的太复杂了,其实挺简单的,关键就是决定转向的时候繁琐一点。
AC代码:

/*
**Author:skj
**Time:
**Function:
*/
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
//0上1右2下3左
void print(int &x1,int x2)
{
    int n;
    if(x1 < x2)
    {
        n = x2 - x1;
    }
    else
    {
        n = x1 - x2;
    }
    for(int i = 1;i <= n;i++)
    {
        printf("f");
    }
    x1 = x2;
}
int turnround(int x1,int y1,int a,int x2,int y2)
{
    if(y2 > y1)
    {
        if(x2 > x1) //终点在起点右上
        {
            if(a == 3)//如果方向朝左顺时针转
            {
                cout << 'c' ;
                return 0;
            }
            else if(a == 2)//下
            {
                cout << 'u' ;
                return 1;
            }
        }
        else if(x2 < x1)//终点在起点左上
        {
            if(a == 1)//起点方向朝右
            {
                cout << 'u';
                return 0;
            }
            else if(a == 2)//下
            {
                cout << 'c';
                return 3;
            }
        }
        else//正上方
        {
            if(a == 1)
            {
                cout << 'u' ;
            }
            else if(a == 2)
            {
                cout << "uu" ;
            }
            else if(a == 3)
            {
                cout << 'c';
            }
            return 0;
        }
    }
    else if(y2 < y1)
    {
        if(x2 > x1)//右下
        {
            if(a == 3)//起点方向朝左
            {
                cout << 'u';
                return 2;
            }
            else if(a == 0)//上
            {
                cout << 'c';
                return 1;
            }
        }
        else if(x2 < x1)//左下
        {
            if(a == 0)//上
            {
                cout << 'u';
                return 3;
            }
            else if(a==1)
            {
                cout << 'c';
                return 2;
            }
        }
        else//正下方
        {
            if(a == 0)
            {
                cout << "cc";
            }
            else if(a == 1)
            {
                cout << 'c';
            }
            else if(a == 3)
            {
                cout << 'u';
            }
            return 2;
        }
    }
    else
    {
        if(x2 < x1)//正左方
        {
            if(a == 0)
            {
                cout << 'u';
            }
            else if(a == 1)
            {
                cout << "cc";
            }
            else if(a == 2)
            {
                cout << 'c';
            }
            return 3;
        }
        else if(x2 > x1)
        {
            if(a == 0)
            {
                cout << 'c';
            }
            else if(a == 2)
            {
                cout << 'u';
            }
            else if(a == 3)
            {
                cout << "cc";
            }
            return 1;
        }
    }
    return a;
}
int main()
{
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int t;
	scanf("%d",&t);
	while(t--)
    {
        int x, y, d,x1,y1;
        scanf("%d%d%d",&x,&y,&d);
        int n;
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d%d",&x1 ,&y1);
            for(int i = 1;x!=x1||y!=y1;i++)
            {
                d = turnround(x,y,d,x1,y1);
                if(d%2)
                {
                    print(x,x1);
                }
                else
                {
                    print(y,y1);
                }
            }
        }
        printf("\n");
    }
    return 0;
}

1012 Yiwen with Sqc 统计贡献,差分优化

题目地址1012 Yiwen with Sqc
题意:给一个字符串,求所有子区间各个字符出现次数之和。
思路:求后缀,因为每增加一个字母,假设这是第i个字母,那么整个字符串的子串数量就会加i个,这时候只要计算这i个新增字串对答案的贡献即可,这样我们的时间复杂度是O(26n),我看题解说会卡常,但我们没有卡。。
AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <unordered_map>
#include <map>
#include <set>
#include <numeric>
#include <stack>
#include <sstream>
#include <cmath>
#include <bitset>
#include <unordered_set>
#include <functional>
#include <list>
#include <vector>
#include <iterator>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 Bint;
const int maxn=1e5+10;
const int mod=998244353;
int T;
ll square[27],sum[27],cnt[27];
char s[maxn];
int main() {
    scanf("%d",&T);
    getchar();
    while(T--) {
        scanf("%s",s+1);
        int len=strlen(s+1);
        ll ans=0;
        for(int i=1; i<=len; i++) {
            int t=s[i]-'a';
            if(i==1) {
                square[t]++;
                sum[t]++;
                cnt[t]++;
                continue;
            } else {
                for(int j=0; j<=26; j++)
                    ans=(ans+square[j])%mod;
                square[t]=(square[t]+(2*sum[t]%mod)+i)%mod;
                cnt[t]=i;
                sum[t]=(sum[t]+i)%mod;
            }
        }
        for(int i=0; i<=26; i++)
            ans=(ans+square[i])%mod;
        printf("%lld\n",ans);
        memset(square,0,sizeof(square));
        memset(sum,0,sizeof(sum));
        memset(cnt,0,sizeof(cnt));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值