“华为杯“ 武汉大学21级新生程序设计竞赛错题笔记

官网链接

D 和谐之树

题目描述
在小马利亚的历史中,和谐之树曾经是大公主和二公主用来甩锅的道具,不过当 M6(暮光闪闪Twilight Sparkle,小蝶Fluttershy 等六位主角)在无尽森林开启它之后,这棵树就一直被暮暮保管着。
这天,暮暮(暮光闪闪)从白胡子星璇留下的资料里学习到了算法的魔法,于是她请小蝶把和谐之树改造成了二叉树的形态,她按照星璇所说,对一段区间建立了这颗树,并且在每个节点上都留下了一个编号。
形式化的,她通过调用Build(1,1,n) 建立了这棵树。
在这里插入图片描述
其中,第一个参数 idid 表示当前节点的编号,后两个参数 l,r 表示当前节点所代表的区间。
暮暮突然想考察一下穗龙(Spike)对于二叉树的结构了解的怎么样,于是要求穗龙回答若干次对 [1,ni] 建立这棵树时,编号最大的节点编号是多少,穗龙不会,只好向你求助。

输入描述:
输入文件的第一行包含一个正整数T(1 ≤ T ≤ 105),表示测试数据的数目。
每个测试数据占单独的一行,包含一个正整数n(1 ≤ n ≤ 1018),表示对区间 [1,n]按如上方式建立改造和谐之树。
输出描述:
对于每个测试数据,在单独的一行内输出结果。
示例一:
输入:
3
2
3
7
输出:
3
5
13
说明:
在这里插入图片描述
样例二:
输入:
5
10
18
36
33
20
输出:
25
49
113
65
57

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

ll ans;

ll dep(ll x)
{
    int d = 1;
    while (x > 1)
    {
        x = (x + 1) >> 1;
        d++;
    }
    return d;
}

void build(ll l,ll r,ll id)
{
    ans = max(ans,id);
    if(l == r)  return;
    ll mid = l + r >> 1;
    if(dep(mid - l + 1) > dep(r - mid))
        build(l,mid,id << 1);
    else    build(mid + 1,r,id << 1 | 1);
}

int main()
{
    int t;
    scanf("%d",&t);
    while (t--)
    {
        ans = 0;
        ll n;
        scanf("%lld",&n);
        build(1,n,1);
        printf("%lld\n",ans);
    }
    return 0;
}

F 仓鼠与炸弹

仓鼠现在在仓鼠通用安保科技公司(Hamster Universial Security Tech, Aka: HUST)工作,这天它接到了一个紧急任务------有人在武汉仓鼠大学(Wuhan Hamster University, Aka: WHU)安放了一枚炸弹!
小仓鼠火速赶到现场,发现现场有一个定时炸弹,炸弹上有一排接口,总计 nn 个。每一种接口都有一个颜色。炸弹的旁边,有一根并口线。并口线的两头是一样的,其中一头可以同时接通连续的 m 个接口,也就是如果把并口线的两头同时接在炸弹上,总共可以接通 2m 个接口。
并口线和炸弹的简单示意图:
在这里插入图片描述

此外,凶手还留下了一张字条,上面是这么写的:
如果想拆弹,就把并口线的两头全部接在炸弹上,不过必须满足一个要求,否则你会当场上天!两个接头各自联通的那一段长度为 mm 的接口,必须是对称的,而且接口不能重叠!嘿嘿嘿,我猜仓鼠的脑容量肯定无法理解我说的话,来,我给你一个例子,如果你的接口大小是2,那么下面这几种接法,除去最后一种都是合法的!
在这里插入图片描述
炸弹的背后,是一个密码锁,正确的密码是你接线的方案数。注意:这里的方案数仅仅讨论被接入的接口的位置哦,也就是说如果你只是交换两个接头的位置,并不算两种不同的方案。加油,慢慢尝试吧,总能试出来的呢!
wk 敬上

时间紧迫,快来帮助小仓鼠拆除炸弹吧!

输入描述:
第一行由两个整数 n,m (1≤n,m≤105),表示炸弹的接口长度,和并口线的接口宽度 m。
第二行一个含有 n 个字符的字符串 s,只包含小写字母,表示炸弹的接口,同一种颜色的接口用同一种字母表示。
输出描述:
一个正整数,表示答案数。
示例一:
输入:
5 1
abbab
输出:
4
示例二:
输入:
7 2
abbabab
输出:
3
示例三:
输入:
10 2
abababbbab
输出:
7

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define please return
#define ac 0

const int N = 1e5+10,P = 131;
const ll mod = 1e14 + 7;//unsigned long long的自然溢出被卡掉了,只好使用mod了
int n,m;
char s[N];
ull h1[N],h2[N],p[N];
unordered_map<ull,ull> mp;

ull get(ull *h,int l,int r)//第一个参数的使用可以省写一个函数!
{//unsigned long long最多也就64位,而int128更强(如果去掉(__int128_t)的强制转换就过不了了)
	return (h[r] % mod - (__int128_t)h[l - 1] * p[r - l + 1] % mod + mod) % mod;
}

signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin >> n >> m >> s + 1;

	p[0] = 1;//初始化
	for(int i = 1;i <= n;i++)
	{//都取个模
		p[i] = p[i - 1] * P % mod;
		h1[i] = (h1[i - 1] * P % mod + s[i]) % mod;//正向哈希
		h2[i] = (h2[i - 1] * P % mod + s[n - i + 1]) % mod;//反向哈希(它的第一个是原字符串的最后一个)
	}
	//将h2从第一个开始,到临界恰好与正向的h1存在交点时结束
	for(int i = 1;i + m - 1 <= n - m + 1;i++)	mp[get(h2,i,i + m - 1)]++;

	ll ans = 0;
	for(int i = m;i + m<= n;i++)
	{
		int l1 = i - m + 1,r1 = i;
		int r2 = n - i + 1,l2 = r2 - m + 1;
		mp[get(h2,l2,r2)]--;//每次将与正向h1有交点的对应反向h2去掉一个(循环正向走,那么遍历过的就不会再出现了,故删)
		ans += mp[get(h1,l1,r1)];
	}
	cout << ans << "\n";
	please ac;
}

G 寄寄子的生日

题目描述
"我的梦想是成为马娘偶像!"在逃亡者SISTERS不断发展壮大的同时,醒目飞鹰也在一步步的接近她的梦想。
4 月 4 日,为了庆祝醒目飞鹰的生日,朋友们为她订购了一个大蛋糕。
注视着大快朵颐的醒目飞鹰,联想起她惨淡的文化成绩,荣进闪耀一把抢过蛋糕。
“飞鹰子,答对下面的问题后才可以继续吃蛋糕哦~”
“现在飞鹰子有一串2∼n 的排列 a ,如果两个数 ai ,aj互质,你就可以交换它们的位置。那么飞鹰子你能够在 2 × n 次交换内,将这串排列按从小到大排序吗?”
“寄!”
茫然地抬起头,醒目飞鹰下意识地回答道。
排列:一串 2∼n 的排列指一个长为 n−1 的序列,满足 2 到 n 每个数出现且仅出现一次。
互质:如果两个正整数 a,b 的最大公约数等于 1,那么称这两个正整数是互质的。

输入描述:
第一行一个整数n(2 ≤ n ≤ 103),表示给出一个 2∼n 的排列。
第二行 n−1 个整数 ai(2 ≤ ai ≤ n),表示具体的排列。
输出描述:
第一行输出一个整数 m(0 ≤ m ≤ 2 × n) ,表示交换的次数。
接下来 m 行,每行两个整数 i,j(1 ≤ i,j ≤ n−1),表示交换第 i 个位置上的数和第 j 个位置上的数。
你可以输出任意一个满足上述限制的答案。注意,你不需要最小化 m.
示例一:
输入:
6
4 5 2 6 3
输出:
5
2 1
1 3
3 2
2 5
5 4
说明:
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> pii;

const int N = 1e3+10;
int n;
int a[N];
pii res[2 * N];//答案数组要开两倍,不然会导致段错误!
int idx;

int gcd(int a,int b)
{
    return b ? gcd(b,a % b) : a;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin >> n;
    for (int i = 2; i <= n; i++)    cin >> a[i];
    for (int i = 2; i <= n; i++)
    {
        if(a[i] != i)
        {
            int pos1 = 0,pos2 = 0;//两个交换位置的下标
            for (int j = i + 1; j <= n; j++)
            {
                if(a[j] == i)   pos1 = j;//找到该位置该放的数的下标
                if(gcd(a[i],a[j]) == 1 && gcd(a[j],i) == 1) pos2 = j;//如果三角关系都互质,可利用中介交换
            }
            if(pos2 != 0)//如果可以采用中介交换,使用是最稳妥的
            {
                res[idx++] = {i - 1,pos2 - 1};
                swap(a[i],a[pos2]);
            }
            swap(a[i],a[pos1]);
            res[idx++] = {i - 1,pos1 - 1};
        }
    }
    cout << idx << endl;
    for (int i = 0; i < idx; i++)
    {
        cout << res[i].first << " " << res[i].second << endl;
    }
    // for(int i = 2;i <= n;i++)   cout << a[i] << " ";
    return 0;
}

J 传闻档案

题目描述
这是发生在Magius之翼刚把鹤乃,大锤和莎奈洗脑后的故事。
成为了队伍的队长,发下了绝不辜负八千代的誓言的彩羽希望能够找到 Magius 的据点,救回三人。
她分析着八千代的神滨传闻档案,漂亮的笔记中有着至今为止打倒的传闻,有着整理过后也没有调查的传闻,有着只知道名字和地点的传闻。
结合着信息的可靠程度与位置关系,彩羽在神滨地图上与八千代一笔一划地标记着。
她们认为,传闻的出现地点共有 n 个,第 i 个地点中传闻有预测魔力值 ai
传闻并不是孤立的,其中有 m 对传闻能够单向地传递着魔力,这使得每个地点实际的魔力表现值更强。具体的,如果第 i 个地点的传闻,能够通过若干条单向魔力通道到达第 j 个地点,它就能够表现出 aj​的魔力值。
传闻们并不傻,它们总会表现出它们能够通过单向通道得到的最大魔力值,彩羽称这个最大值为表现魔力值。
彩羽想知道所有传闻的表现魔力值之和是多少,象征性的,她把目光投向了身为小qb的你。
输入描述:
第一行输入两个正整数 n,m(1 ≤ n ≤ 105 ,1 ≤ m ≤ 3×105) ,分别表示传闻出现地点个数和魔力通道个数。
第二行输入 n 个正整数a1,…,an(1 ≤ ai ≤ 109 ),其中 ai​表示编号为 i 的传闻的预测魔力值。
接下来 m 行,每行两个整数 ui​,vi(ui != vi),表示一条魔力通道。
保证不存在两条完全相同的魔力通道。
输出描述:
输出一行,包含一个整数,表示所有传闻的表现魔力值之和。
示例一:
输入:
4 4
2 1 4 3
1 2
1 3
2 4
3 4
输出:
14
示例二:
输入:
8 9
2 1 1 1 1 1 1 1
2 1
2 4
4 3
3 2
5 4
5 7
7 8
8 5
6 8
输出:
16
备注:
在这里插入图片描述

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

typedef long long ll;

using namespace std;

const int N = 3e5+10;

int n,m;
int a[N];
int h[N],e[N],ne[N],idx;
int dfn[N],low[N],timestamp;
int stk[N],top;
bool in_stk[N];
int scc_cnt;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++ timestamp;
    stk[++top] = u,in_stk[u] = true;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u],low[j]); 
        }
        else if(in_stk[j])  low[u] = min(low[u],dfn[j]);
        a[u] = max(a[u],a[j]);
    }
    if(dfn[u] == low[u])
    {
        ++scc_cnt;
        int y;
        do{
            y = stk[top--];
            in_stk[y] = false;
            a[y] = a[u];
        }while(y != u);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++)   scanf("%d",&a[i]);
    memset(h,-1,sizeof(h));
    while (m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    
    for(int i = 1;i <= n;i++)
        if(!dfn[i]) tarjan(i);
    ll ans = 0;
    for(int i = 1;i <= n;i++)   ans += a[i];
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值