hihocoder 编程练习赛79(只有123, 4正在更新)

hihocoder 编程练习赛79

目录

hihocoder 编程练习赛79

题目1 : 字母去重

1.题是什么?

2.思路

3.ac代码

题目2 : D级上司

1.题是什么?

2.思路

3.ac代码

题目3 : 数组的F值

1.题是什么?

2.思路

(1)INS操作

(2).DEL操作

3.ac代码


 

题目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原先为(2-1)^{2}+(3-2)^{2}+(5-3)^{2}+(7-5)^{2}

    插入3后对于1 2 3 3 5 7 fa为(2-1)^{2}+(3-2)^{2}+(3-3)^{2}+(5-3)^{2}+(7-5)^{2}

    (3-3)^{2}必然等于0,故而我们发现当插入的x在原set中已存在时是不会改变fa值的,由于重复值是无效的故而我们甚至不用将值插入set.

 

插入值在原set中不存在的情况下,insert操作返回的会是插入x后set中指向x的迭代器以及true:

    下面以在已排序的B set集合1 2 5 7中INS 3为例进行分析

    fa原先为(2-1)^{2}+(5-2)^{2}+(7-5)^{2}

    插入3后对于1 2 3 5 7fa为(2-1)^{2}+(3-2)^{2}+(5-3)^{2}+(7-5)^{2}

    注意细节会发现前后不变,变的仅仅是2 5与2 3 5的fa计算,故而我们对于插入操作仅考虑插入值与其前后值的关系改变就好,假如插入值x前一个值为lower,后一个值为upper,fa的改变为加上(upper-x)^{2}+(x-lower)^{2}-(upper-lower)^{2},这里需要注意一个细节,就是如果ins的值在序列中是最小值或是最大值怎么办,这样它就可能没有lower或upper,甚至如果序列为空,可能两个都没有,故而要特殊处理一下,见代码,

(2).DEL操作

    下面以在已排序并去重的B set集合1 2 3 5 7中DEL 3为例进行分析(如果del的是不存在的数则可直接输出fa,进行下一个操作)

     fa原先为(2-1)^{2}+(3-2)^{2}+(5-3)^{2}+(7-5)^{2}

     删除3后对于1 2 5 7 fa为(2-1)^{2}+(5-2)^{2}+(7-5)^{2}

     就是ins原先不存在的值时的逆向,fa的改变自然显而易见为加上(upper-lower)^{2}-(upper-x)^{2}-(x-lower)^{2},前面加个负号而已.依然对于是最大值或最小值时特殊处理一下.之后将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;
}

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值