hihocoder 编程练习赛79
目录
题目1 : 字母去重
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个字符串S,每次操作你可以将其中任意一个字符修改成其他任意字符。
请你计算最少需要多少次操作,才能使得S中不存在两个相邻的相同字符。
输入
只包含小写字母的字符串S。
1 ≤ |S| ≤ 100000
输出
一个整数代表答案
样例输入
aab
样例输出
1
1.题是什么?
给你一个字符串,每次操作可以改任意一个字符为任意字符,最少多少次操作,可以使字符串不存在相邻相同字符
2.思路
贪心的从左到右的改,唯一一个坑是对于字符串aaa你可以将中间的a改成b,这样只需要一次操作就能完成目的,如果你的程序能过掉aaa这个样例,就能ac了.故而我们贪心的方法应该是判断这个字符和前一个字符是否相同.
3.ac代码
#include <stdio.h>
void solve(){
char a[100005];
scanf("%s",&a);
int pos=1,ans=0;
while(a[pos]){
if(a[pos]==a[pos-1]){
a[pos]='?';
ans++;
}
pos++;
}
printf("%d\n",ans);
}
int main(){
solve();
return 0;
}
题目2 : D级上司
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
H公司一共有N名员工,编号为1~N,其中CEO的编号是1。除了CEO之外,每名员工都恰好有唯一的直接上司;N名员工形成了一个树形结构。
我们定义X的1级上司是他的直接上司,2级上司是他上司的上司,以此类推……
请你找出每名员工的D级上司是谁。
输入
第一行包含2个整数N和D。
以下N-1行每行包含一个整数,依次代表编号是2-N的员工的直接上司的编号。
对于50%的数据,1 ≤ N, D ≤ 10000
对于100%的数据,1 ≤ N, D ≤ 100000
输出
依次输出1~N的D级上司的编号,每个一行。如果某员工没有D级上司,输出-1。
样例输入
5 2
1
1
3
3
样例输出
-1
-1
-1
1
1
1.题是什么?
给你n-1个员工的直接隶属关系,即为每个员工唯一的直接上司,你要输出的是每个员工的d级上司.
2.思路
这是一个树型关系,故而想到dfs路径保存来解决,至于树形关系的存储使用vector实现的邻接表,这样dfs到每个员工时通过沿途保存的路径我能够知道他的所有的上级间接上司,自然也能知道是否有d级,是谁,
复杂度方面:
空间上路径数组只需要最多maxn,vector虽然大小maxn,可是由于是树,相当于稀疏图,vector也不会爆,递归的深度在极限数据下可能达到maxn造成爆栈,然而我交上去试了一下ac了,故而不对此问题进行优化
时间上每个结点的处理复杂度为1,每个结点也只会处理一次,故时间不足为虑.
3.ac代码
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
const int maxn=100005;
vector<int> v[maxn];
int path[maxn],deep;
int ans[maxn];
void dfs(int serial,int d){
if(deep<d) ans[serial]=-1;
else ans[serial]=path[deep-d];
path[deep++]=serial;
int size=v[serial].size();
for(int i=0;i<size;i++) dfs(v[serial][i],d);
deep--;
}
void solve(){
int n,d;
scanf("%d%d",&n,&d);
for(int i=2;i<=n;i++){
int t;
scanf("%d",&t);
v[t].push_back(i);
}
deep=0;
dfs(1,d);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
int main(){
solve();
return 0;
}
题目3 : 数组的F值
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
我们定义一个数组A = [A1, A2, ... AN]的F值F(A)是:
将A从小到大排序得到数组B = [B1, B2, ... BN],F(A) = (B2 - B1)2 + (B3 - B2)2 + ... + (BN - BN-1)2
现在给定一个长度为N的数组A,然后依次对A进行M项操作。每项操作是以下2种之一:
INS X:插入一个值为X的元素
DEL X:删除所有值为X的元素
请你计算每次操作之后的F(A)
输入
第一行包含两个整数N和M。
第二行包含N个整数A1, A2, ... AN。
以下M行每行一个操作。
对于50%的数据,1 ≤ N, M ≤ 1000
对于100%的数据,1 ≤ N, M ≤ 100000 1 ≤ X, Ai ≤ 100000
输出
对于每一项操作输出操作之后的F(A),每个一行。
样例输入
5 3
1 2 4 5 6
INS 3
INS 4
DEL 4
样例输出
5
5
7
1.题是什么?
给你n个数字的数组a,让你将它排序成b数组,然后计算一个fa值,fa=(B2 - B1)^2 + (B3 - B2)^2 + ... + (BN - BN-1)^2,
之后让你对a数组做m次操作 ,操作可能是以下两种之一
INS X:插入一个值为X的元素
DEL X:删除所有值为X的元素
每次操作完了之后输出此时a数组的fa值.
2.思路
首先关于fa的求得,我们可以发现对a无论升序还是降序排序得到的答案fa是相同的,故而我采用升序.另外我们从fa公式中会发现重复的值是不改变最终fa值的,我们这里直接将数据压入set就好,set会维持升序,还会自动去重.
然后下面分析两种操作对fa值的影响:
(1)INS操作
插入值x在原set中已存在的情况下,insert操作返回的会是原set中指向x的迭代器以及false:
下面以在已排序并去重的B set集合1 2 3 5 7中INS 3为例进行分析
fa原先为
插入3后对于1 2 3 3 5 7 fa为
必然等于0,故而我们发现当插入的x在原set中已存在时是不会改变fa值的,由于重复值是无效的故而我们甚至不用将值插入set.
插入值在原set中不存在的情况下,insert操作返回的会是插入x后set中指向x的迭代器以及true:
下面以在已排序的B set集合1 2 5 7中INS 3为例进行分析
fa原先为
插入3后对于1 2 3 5 7fa为
注意细节会发现前后不变,变的仅仅是2 5与2 3 5的fa计算,故而我们对于插入操作仅考虑插入值与其前后值的关系改变就好,假如插入值x前一个值为lower,后一个值为upper,fa的改变为加上,这里需要注意一个细节,就是如果ins的值在序列中是最小值或是最大值怎么办,这样它就可能没有lower或upper,甚至如果序列为空,可能两个都没有,故而要特殊处理一下,见代码,
(2).DEL操作
下面以在已排序并去重的B set集合1 2 3 5 7中DEL 3为例进行分析(如果del的是不存在的数则可直接输出fa,进行下一个操作)
fa原先为
删除3后对于1 2 5 7 fa为
就是ins原先不存在的值时的逆向,fa的改变自然显而易见为加上,前面加个负号而已.依然对于是最大值或最小值时特殊处理一下.之后将x从set中真正删除,保持数据有效性.
后续更新:最开始由于使用数组保存数据使得数据有效性的保持用了n复杂度,导致总复杂度n*n造成tle,现在用set保存数据,由于set底层是由二叉搜索树实现,对于单个数据的增删复杂度为logn,总复杂度nlogn已ac
3.ac代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <set>
using namespace std;
typedef long long ll;
const int maxn=100005;
set<ll> s;
set<ll>::iterator ite,ite1,ite2;
void solve(){
int n,m;
scanf("%d%d",&n,&m);
//set的插入操作本身会去重
for(int i=0;i<n;i++){
int t;
scanf("%d",&t);
s.insert(t);
}
//初始fa
ll fa=0;
ite=s.begin();
ite1=ite;
ite2=++ite;
while(ite2!=s.end()){
fa+=(*ite2-*ite1)*(*ite2-*ite1);
ite1=ite2;
++ite2;
}
//操作
char order[10];
ll number;
for(int i=0;i<m;i++){
scanf("%s%lld",&order,&number);
if(!strcmp(order,"INS")){
pair<set<ll>::iterator,bool> res=s.insert(number);
if(res.second){
ite=res.first;
ite2=ite;ite2++;//位置加一自然就是正好比number小的数
ite1=ite;ite1--;//位置减一自然就是正好比number小的数
if(ite!=s.begin()){
fa+=(number-*ite1)*(number-*ite1);
if(ite2!=s.end()){
fa-=(*ite2-*ite1)*(*ite2-*ite1);
}
}
if(ite2!=s.end()){
fa+=(*ite2-number)*(*ite2-number);
}
}
}
else if(!strcmp(order,"DEL")){
ite=s.find(number);//以logn复杂度找到大于等于number的第一个数的位置
if(ite!=s.end()){
ite2=ite,ite2++;//此时number还没删除,故而ite此时指向number,需要++才是正好比number大的数
ite1=ite,ite1--;//减一就是正好比number小的数
if(ite!=s.begin()){
fa-=(number-*ite1)*(number-*ite1);
if(ite2!=s.end()){
fa+=(*ite2-*ite1)*(*ite2-*ite1);
}
}
if(ite2!=s.end()){
fa-=(*ite2-number)*(*ite2-number);
}
s.erase(ite);
}
}
printf("%lld\n",fa);
}
}
int main(){
solve();
return 0;
}