Codeforces Round #359 (Div. 2) 部分题解

之前两场连续的崩盘让分数又回到了DIV2底层,挺难过的。

昨晚发挥的还不错,继续加油吧。


Codeforces 686A Free Ice Cream

题目链接:http://www.codeforces.com/problemset/problem/686/A

题意:N个人在排队,一开始店主有M个冰淇凌,队伍中第i个人可能是一个送冰激凌的工人,他会带给店主A[i]个冰淇凌,也可能是一个小朋友,他想要A[i]个冰淇凌,当排到一个小朋友的时候,如果店主现在手里的冰淇凌数量能满足他,那么就满足他,不然小朋友就会一个冰淇凌也不拿失望的离开。

问队伍中所有的人都走了之后店主剩下的冰淇凌数量和失望离开的小朋友的数量。

分析:模拟操作即可,需要注意的是数据要开long long 或者__int64。


/***********************************************
 |Author: Fry
 |Created Time: 2016/6/24 0:37:21
 |File Name: A.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;

int main()
{
	long long n,x,ans,a;
	char c;
	while (~scanf("%I64d%I64d",&n,&x)){
		ans=0;
		for (int i=1;i<=n;i++){
			scanf(" %c%I64d",&c,&a);
			if (c=='+') x+=a;
			else {
				if (x>=a) x-=a;
				else ans++;
			}
		}
		printf("%I64d %I64d\n",x,ans);
	}
	return 0;
}

Codeforces 686B Little Robber Girl's Zoo

题目链接:http://www.codeforces.com/problemset/problem/686/B

题意:一群动物排成一排,各有各的身高,要用两万次以内的操作让整个队伍变成升序。

每次操作可以选择连续的长度为偶数的区间,让其中的动物一次配对并与相邻的动物交换位置。比如,当选择区间[1,4]时,会让位置在1的动物和位置在2的动物交换,以及位置在3的动物和位置在4的动物交换。

动物数量不超过100只。

分析:冒泡排序的交换次数最多不超过n×(n-1)/2 ,所以直接冒泡排序记录次数即可。

/***********************************************
 |Author: Fry
 |Created Time: 2016/6/24 0:44:15
 |File Name: B.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;
void swap(int &a,int &b)
{
	int c=a;
	a=b;
	b=c;
}
vector<int>va,vb;
int a[105];
int main()
{
	int n;
	while (~scanf("%d",&n)){
		for (int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		va.clear(); vb.clear();
		for (int i=n;i>=1;i--){
			for (int j=1;j<i;j++){
				if (a[j]>a[j+1]){
					va.push_back(j);
					vb.push_back(j+1);
					swap(a[j],a[j+1]);
				}
			}
		}
	//	for (int i=1;i<=n;i++) printf("%d ",a[i]); printf("\n");
		for (int i=0;i<va.size();i++){
			printf("%d %d\n",va[i],vb[i]);
		}
	}
	return 0;
}



Codeforces 685A Robbers' watch

题目链接:http://www.codeforces.com/problemset/problem/685/A

题意:在另一个世界观下,一天被分为n个小时,一个小时被分为m分钟。

一个人有一块表,表上的数字是7进制的,会显示时(0到n-1)和分(0到m-1)。

现在问一天之中有多少分钟能让表上显示的数字都不相同。比如1:0就是合法的,1:1就是不合法的。

需要注意的是前导0不可以省略。比如n=8,m=2时,1时1分将会表示成01:1;当n=60,m=7时,1时1分将会表示成001:1;

现在给你n和m,求结果,即一天之中有多少分钟能让表上显示的数字都不相同。

n和m的范围<=1e9.


分析:

首先可以知道每个显示数字最大不会超过6(因为是7进制)

时和分分开来考虑,枚举每一个数字是否合法,并用2进制来表示出现了哪些数字。比如0000101表示出现了2和0两个数字,储存好。

之后枚举单独合法的时和分,数一下有多少组合是合法的就可以了。

虽然n和m的范围1e9,但是由于是7进制,所以显示的数字最大不会超过7进制下的6543210,也就是10进制下的800667。最终可能的结果数不会超过7!+6!+5!+4!+3!+2!+1!,大概不超过6000。两边分别枚举也不会超时。

/***********************************************
 |Author: Fry
 |Created Time: 2016/6/24 0:56:15
 |File Name: C.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;

vector<int>v[2];
void check(int n,int x,int num)
{
    int ans=0,f=n;;
    for (int i=1;i<=num;i++)
    {
        if ((ans&(1<<(n%7)))) return ;
        ans|=(1<<(n%7));
        n/=7;
    }
    v[x].push_back(ans);
}
void DW(int n,int x)
{
    n=min(n,1000000);
    int tem=n-1,num=0;
    if (tem==0) num=1;
    else 
    {
        while (tem){
            num++;
            tem/=7;
        }
    }
    for (int i=0;i<n;i++){
        check(i,x,num);
    }
}
int main()
{
    int n,m,ans;
    while (~scanf("%d%d",&n,&m)){
        v[0].clear();v[1].clear();
        DW(n,0); DW(m,1);
        ans=0;
        for (int i=0;i<v[0].size();i++){
            for (int j=0;j<v[1].size();j++){
                if ((v[0][i]&v[1][j])==0) ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}



Codeforces 685B Kay and Snowflake

题目链接:http://www.codeforces.com/problemset/problem/685/B

题意:已知一颗节点数为n,根为1的树,一共有q个询问,每次询问一个节点x,针对每个询问要给出所询问的节点的子树的重心。

n,q的范围3e5


分析:

树的重心的定义题目中给出了:去掉这个节点后剩下的每一个连通分量的节点数都不超过原来的树的一半。

递归预处理出所有树的重心就可以实现O(1)询问了。

预处理:

首先一边DFS处理出所有的子树的大小。

然后进行第二次DFS,当处理到节点X时,首先先处理掉所有X的子节点,

然后讨论:

1.如果X为重心,那么去掉X之后剩下的所有连通分量就是X的子节点以下的子树,它们之中最大的子树应当不超过X形成的树的一半。

2.如果1不成立,那么重心一定在X的子节点所形成的子树中最大的那一棵子树Y中,并且重心的位置一定是Y的重心的祖先节点,向上搜索一遍。


由于处理的时候所有的节点都最多只被考虑2次,所以预处理复杂度O(n),查询复杂度O(1)


/***********************************************
 |Author: Fry
 |Created Time: 2016/6/24 1:49:32
 |File Name: D.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;
const int N=3e5+5;
vector<int>v[N];
int s[N],maxson[N],ans[N],f[N];

void getsize(int x)
{
	int maxn=-1;
	s[x]=1;
	for (int i=0;i<v[x].size();i++){
		getsize(v[x][i]);
		s[x]+=s[v[x][i]];
		if (maxn==-1||s[v[x][i]]>s[maxn]) maxn=v[x][i];
	}
	maxson[x]=maxn;
	return ;
}

void cal(int x)
{
	if (s[x]==1){
		ans[x]=x;
		return ;
	}
	int tem=-1;
	for (int i=0;i<v[x].size();i++){
		cal(v[x][i]);
		if (s[v[x][i]]*2>s[x]) tem=ans[v[x][i]];
	}
	if (tem!=-1){
		while ((s[x]-s[tem])*2>s[x]){
			tem=f[tem];
		}
		ans[x]=tem;
	}
	else {
		ans[x]=x;
	}
	return ;
}

int main()
{
	int n,q,x;
	while (~scanf("%d%d",&n,&q)){
		for (int i=1;i<=n;i++) v[i].clear();
		for (int i=2;i<=n;i++){
			scanf("%d",&x);
			v[x].push_back(i);
			f[i]=x;
		}
		getsize(1);
		cal(1);
		for (int i=1;i<=q;i++){
			scanf("%d",&x);
			printf("%d\n",ans[x]);
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值