HPU暑期集训积分赛5

A题:

时间限制 内存限制 出题人
1 Second 512 Mb lzs
题目描述
有一个串S,定义任意串B的花费cost(B)为:需要添加几个字符可以让B的一个后缀串等于S.
eg: S = ”abc”
B = ”aaa”, cost(B) = 2, 需要添加两个字符0
b
0和0
c
0
,B = ”aaabc”,使得B的一个后缀为S(”abc”)
C = ”abc”, cost(C) = 0, 不需要添加字符就可以让C的一个后缀为 S(”abc”)
问题:现给一个串S和一个串T
求所有的cost(Tj ) (对于一个串T,T的第1个字符到第j个字符所构成的新串定义为Tj
.)
输入
第一行一个整数T表示测试组数 (T ≤ 5)
接下来每组测试数据,两个串S和T
(0 < strlen(S), strlen(T) <= 200000, S和T中都只有小写字母)
输出
每组测试数据都是:
输出strlen(T)行
第j行输出一个整数表示cost(Tj ).(1 ≤ j ≤ strlen(T))
输入样例
2
abc aabc
aaaaa aa
输出样例
2
2
1
0
4
3

思路:

对于字符串T,我们预处理出cost[ i ],之后直接用len s减就行了

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = (int)2e5 + 10;
char s1[maxn],s2[maxn];
int nxt[maxn];
void init(int n,int m)
{
    memset(nxt,0,sizeof(nxt));
    int i = 0,j = 0;
    while (i < m)
    {
        if (s1[j] == s2[i])
            nxt[i ++] = ++j;
        else if(!j)
        {
            nxt[i ++] = 0;
        }
        else
        {
            j --;//回溯 
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while (t --)
    {
        scanf("%s %s",s1,s2);
        int len1 = strlen(s1),len2 = strlen(s2);
        init(len1,len2);
        for (int i = 0;i < len2;i ++)
            printf("%d\n",len1 - nxt[i]);
    }
    return 0;
}

F题:

F 辞树的质因数分解
时间限制 内存限制 出题人
1 Second 512 Mb 吴丰源
题目描述
给出一个正整数将其质因数分解的结果按照标准格式打印出来
输入
一个正整数 T。代表有T组数据 (T ≤ 10)
每组数据有一个非负整数n ( 0 ≤ n ≤ 1000000)。
输出
若n能进行因式分解则按照标准格式讲质因数分解的结果打印出来,否则输出n。每组数据需要
换行。
输入样例
5
0
1
2
4
10
输出样例
0
1
2=2^1
4=2^2
10=2^1*5^1

思路:

这道题很简单,一道基础的签到题

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = (int)1e6 + 5;
bool check[maxn];
int prime[maxn];
int pos = 0;
void init()
{
    memset(check,0,sizeof(check));
    for (int i = 2;i < maxn;i ++)
    {
        if (!check[i])
        {
            prime[pos ++] = i;
            for (int j = i + i;j < maxn;j *= i)
                check[j] = 1;
        }

    }
}
int main()
{
    int t,n;
    init();
    scanf("%d",&t);
    while (t --)
    {
        map<int,int> mp;
        scanf("%d",&n);
        if (!check[n] || n == 0 || n == 1)
        {
            printf("%d\n",n);
            continue;
        }
        for (int i = 0;prime[i] * prime[i] <= n;i ++)
        {
            while (n % prime[i] == 0)
            {
                mp[prime[i]] ++;
                n /= prime[i];
            }
        }
        if (n != 1)
            mp[n] = 1;
        map<int,int> :: iterator it;
        int i = 0;
        for (it = mp.begin();it != mp.end();it++)
        {
            int p = it -> first;
            if (!i)
                printf("*");
            printf("%d^%d",p,mp[p]);
            i ++;
        }
        putchar('\n');
    }
    return 0;
}

G 迷宫:


时间限制 内存限制 出题人
1 Second 512 Mb 朱梓鑫
题目描述
有一个n*m大小的迷宫,s代表起点,t代表终点.表示可以经过,表示墙,即不可经过。只
能走上下左右四个方向。求起点到终点的最短路径。
输入
第一行两个整数N, M表示迷宫大小为N * M,见样例.(1 ≤ N,M ≤ 100)
接下来是一个N * M迷宫
处理到文件结束
输出
每行输出s到t的最短路径.无法到达请输出-1
输入样例
5 5
s#...
.#.#.
.....
.#.#.
...#t
输出样例
8

思路:

bfs板子题

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 110;
char maze[maxn][maxn];
bool vis[maxn][maxn];
int n,m;
int dx[4] = {1,0,0,-1};
int dy[4] = {0,1,-1,0};
struct node
{
    int x,y,step;
    friend bool operator <(node a,node b)
    {
        return a.step > b.step;
    }
};
void bfs(int bx,int by,int ex,int ey)
{
    memset(vis,0,sizeof(vis));
    vis[bx][by] = 1;
    int ans = inf;
    priority_queue<node> que;
    node p,p2;
    p.x = bx,p.y = by,p.step = 0;
    que.push(p);
    while (!que.empty())
    {
        p = que.top();
        que.pop();
        if (p.x == ex && p.y == ey)
        {
            ans = min(ans,p.step);
        }
        for (int i = 0;i < 4;i ++)
        {
            p2.x = p.x + dx[i],p2.y = p.y + dy[i];
            if (p2.x < 0 || p2.x >= n || p2.y < 0 || p2.y >= m || vis[p2.x][p2.y])
                continue;
            if (maze[p2.x][p2.y] == '#')
                continue;
            p2.step = p.step + 1;
            vis[p2.x][p2.y] = 1;
            que.push(p2);
        }
    }
    if (ans == inf)
        printf("-1\n");
    else
        printf("%d\n",ans);
}
int main()
{
    while (~scanf("%d %d",&n,&m))
    {
        for (int i = 0;i < n;i ++)
            scanf("%s",maze[i]);
        int bx,by,ex,ey;
        for (int i = 0;i < n;i ++)
            for (int j = 0;j < m;j ++)
            {
                if (maze[i][j] == 's')
                    bx = i,by = j;
                if (maze[i][j] == 't')
                    ex = i,ey = j;
            }
        bfs(bx,by,ex,ey);
    }
    return 0;
}

H题:

H Summation of polynomials
时间限制 内存限制 出题人 2 Second 512 Mb 李浩阳
题目描述
给你一个数n,问1+1/2+1/3+……+1/n是多少? 多组输入;
输入
先输入一个变量T (T <= 10000) 代表测试样例的数量 每组测试样例输入一个整数n(1 <= n <= 1e8)
输出
对于每组测试样例,输出样例编号和结果; 结果输出小数点后6位;
输入样例
3

1

5

90000000
输出样例
Case 1: 1.000000

Case 2: 2.283333

Case 3: 18.892535

思路:

这道题什么鬼,还以为高精度什么的,结果样例3是错的,暴力打表就能过了,也是醉了,搞得很烦

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = (int)1e8 + 10;
double f[maxn];
void init()
{
    f[1] = 1;
    for (int i = 2;i < maxn;i ++)
        f[i] = f[i - 1] + 1.0 / i;
}
int main()
{
    int t,kase = 1;
    init();
    scanf("%d",&t);
    while (t --)
    {
        int n;
        scanf("%d",&n);
        printf("Case %d: %.6lf\n",kase ++,f[n]);
    }
    return 0;
}

I 距离:

时间限制 内存限制 出题人
1 Second 512 Mb dnmt008
题目描述
XM从家出发去商店购物,在他家附近有两家商店。从他家到商店1有一条长为d1的道路,从
他家到商店2有一条长为d2的道路;还有一条长为d3的道路,直接把这两家商店连在一起。帮
助XM计算出他从家出发去两家商店购物并返回家中需要走的最小距离
XM总是从他家出发。两个商店他都要光顾,而且只能沿着现有的三条道路移动,然后返回他的
房子。他可以去同一家商店多次,也可以走同一条路多次。唯一的目标是使行程的总距离最小
化。
注意:XM的目标是从家出发光顾两个商店(两个商店都要光顾),并返回家。
注意:d1,d2,d3非直线距离,而是道路长度。道路可以是弯曲的,即数据保证家,商店1,商店2可
以连接在一起。
输入
第一行输入一个整数T(1≤T≤200),代表T组测试样例。
对于每组测试样例输入包含三个整数d1、d2、d3(1≤d1、d2、d3≤100000)-路径的长度
d1是连接XM家和第一家商店的道路的长度;
d2是连接XM家和第二家商店的道路的长度;
d3是连接两个商店的道路的长度。
输出
对于每组测试样例输出XM从家出发光顾两个商店并返回家中所需的最短行程。
输入样例
2
10 20 30
1 1 5
10
输出样例
60
4

思路:

签到题,只有四种走法

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int main()
{
    int d1,d2,d3,t;
    int ans,sum;
    scanf("%d",&t);
    while (t --)
    {
        scanf("%d %d %d",&d1,&d2,&d3);
        ans = d1 + d2 + d3;
        sum = ans * 2;
        sum = sum - 2 * max(max(d1,d2),d3);
        ans = min(ans,sum);
        printf("%d\n",ans);
    }
    return 0;
}

J 最小生成树???

时间限制 内存限制 出题人
1 Second 512 Mb 丁金峰
题目描述
n个点,m条边,每个边会有一个价值。求能连成一棵树的最小价值和?不不不,求这个最小价
值和在n次方情况下对1000000007取模。怎么样,够简单吧!
输入
第一行两个整数n,m(n大于等于1,小于等于1000)(m大于等于0,小于等于100000
接下来m行,每行有两个点i、j(i和j在1到n之间的整数),和一个价值k(k在0和1000之间的整
数)
输出
输出一个对1000000007取模的整数 ,如果连不成一棵树,输出-1
输入样例
4 4
1 2 6
1 3 4
1 4 5
4 2 3
输出样例
20736

思路:

krustal与快速幂的无脑衔接

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 10;
struct node
{
    int u,v,cost;
}a[maxn];
bool cmp(node x,node y)
{
    return x.cost < y.cost;
}
int f[1010];
int find(int x)
{
    if (f[x] != x)
        f[x] = find(f[x]);
    return f[x];
}
ll quick(ll a,int b)
{
    ll ans = 1;
    a = a % mod;
    while(b)
    {
        if (b & 1)
            ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for (int i = 1;i <= n;i ++)
        f[i] = i;
    for (int i = 0;i < m;i ++)
    {
        scanf("%d %d %d",&a[i].u,&a[i].v,&a[i].cost);
    }
    sort(a,a + m,cmp);
    int cnt = 1;
    ll ans = 0;
    for (int i = 0;i < m;i ++)
    {
        if (cnt == n)
            break;
        int fx = find(a[i].u),fy = find(a[i].v);
        if (fx != fy)
        {
            cnt ++;
            ans = ans + a[i].cost;
            f[fx] = fy;
        }
    }
    if (cnt != n)
        printf("-1\n");
    else
        printf("%lld\n",quick(ans,n));
    return 0;
}

K题:

看书
时间限制 内存限制 出题人 1 Second 512 Mb 刘帅坤
题目描述
一本书,有不同的知识点,每一种知识点出现的次数不止一次,若想在一段连续的页数内看完 所有的知识点,求出现最短子序列长度。
输入
多组输入。 输入的第一行是一个整数 n (1≤ n ≤10000),这是书的页数。 第二行包含n个非负整数,描述每个页面的概念。第一个整数是第一个页面的内容,第二个整数 是第二个页面的内容,依此类推.(非负整数都是int范围内) 输入以EOF结束。
输出
输出一行:书中最短的连续部分的长度,其中包含书中涵盖的所有知识点。
输入样例
5 1 8 8 8 1
输出样例
2

思路:

这道题有点亏,其实是很简单的一道尺取,之前还做过来着,竟然忽略了,差不多的一道题可以尝试codeforces701C那道题

首先计算一下知识点的种类数量,定义一个指针 i 指向每种结果的头部,和中间结果记录值res,之后从开始往后爬,每次遇到跟开始时一样的知识点时就更新开始时的知识点(向后移位),当知识点的种类等于全体时就更新一下ans(结果),怕到最后就好了;

代码: 

#include<bits/stdc++.h>
using namespace std;
const int maxn = (int)1e5 + 10;
char a[maxn];
int main()
{
    int n;
    while (~scanf("%d",&n))
    {
        map<int,int> mp;
        int num = 0;
        for (int i = 1;i <= n;i ++)
        {
            scanf("%d",&a[i]);
            if (!mp[a[i]])
            {
                mp[a[i]] = 1;
                num ++;
            }
        }
        mp.clear();
        int cnt = 0,i = 1,ans = n + 1,res = 0;
        for (int j = 1;j <= n;j ++)
        {
            if (!mp[a[j]])
                cnt ++;
            mp[a[j]] ++;
            res ++;
            while (cnt == num && mp[a[i]] > 1 && i <= n)
            {
                res --;
                mp[a[i]] --;
                i ++;
            }
            if (cnt == num)
                ans = min(ans,res);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值