官网链接
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;
}