Tinkoff Internship Warmup Round 2018 and Codeforces Round #475 (Div. 2)

题目链接

A. Splits

(水题)

题意:总结起来就是给出一个n,问你能把它拆分成多少种下面的weight,下面样例更好理解

Eg: n=8    =》  ans=5

Note:weight 8:[1,1,1,1,1,1,1,1]   weight 1:[2,1,1,1,1,1,1]        weight 2:[2,2,1,1,1,1 ]  

           weight 3:[2,2,2,1,1]             weight 4:[2,2,2,2]     (看出规律没有)

题解:根据上面的规律可以得到ans=n/2 + 1 (n/2是判断能合成多少个2,+1是全是1的情况)

代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int n;
	while (~scanf("%d", &n))
		cout << n / 2 + 1 << endl;
	return 0;
}

B. Messages

(水题)

题意:已知某人会收到n个信封,随后给出a,b,c和t (a表示每个信封初始含有的钱,b和c的含义合起来就是你从拿到信封开始一天不打开里面的钱会每天加(c - b)元,可以是负数,t表示你会在t天内把所有信封都打开)随后一行给出n个数,第i个数表示第i个信封你将会在第t[i]t天收到(可以选择打开或者之后再打开),这个人想要在打开n个信封后得到最大的利益。

题解:很明显的结论是当你发现c-b的实际意义后,对于你取到的每个信封就只有2种结果:立刻打开并获得价值a(即b>=c,说明你的信封不可能会升值) ,等到最后一天再打开(即b<c,你的信封会增值)

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn = 1e3 + 500;
int t[maxn];
int main()
{
	int n, a, b, c, T;
	while (~scanf("%d%d%d%d%d", &n, &a, &b, &c,&T))
	{
		for (int i = 0; i < n; i++)
			scanf("%d", &t[i]);
		sort(t, t + n);
		int ans = 0;
		if (b >= c)
			cout << n*a << endl;
		else
		{
			for (int i = 0; i < n; i++)
				ans +=(c - b)*(T - t[i]);
			cout << ans+n*a << endl;
		}
	}
	return 0;
}


C. Alternating Sum

(找规律)

题意:给出一个n,a,b,k,随后一行给出长度为k的字符串s,其中s只含有‘+’和‘-’,然后要你求下面这个等式模1e9+9后的结果,其中当s[i]==‘+’时si=1, 当s[i]==‘-’时si=-1,且s[i]=s[i%k].


                      

题解:由于n最大值有1e9,不可以暴力,1e9+9也不是质数,不能使用等比数列求和公式(因为求不了逆元)下面介绍一种对结果的n个数进行压缩的方法,找规律发现的。先给一个例子,当n=7,a,b随意,k=3,s="+-+"时(先不考虑模运算)那么ans = a^3b^0 - a^6b^1 + a^5b^2

                                               + a^4b^3 - a^3b^4 + a^2b^5

                                               + a^1b^6 - a^0b^7 

                                              = a^6b^0 * (a^2b^0 - a^1b^1 + a^0b^2)

                                               + a^3b^3 * (a^2b^0 - a^1b^1 + a^0b^2)

                                               a^1b^6 - a^0b^7 

发现什么没有?这里的(a^2b^0 - a^1b^1 + a^0b^2)只用算一次,那么我们的n就被压缩了,通过k来压缩,不考虑快速幂的时间复杂度的情况下从O(n)将到了O(n/k),当时如果题目给的k太小,一样要爆炸,那么我们可以先将k扩大,通过题目的s[i]=s[i%k],扩大到大概是srqt(n)左右就可以了,至于实际操作看代码吧。(如果看不懂代码的地方可以自己根据上面给出那组样例即方法模拟)

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define maxn (int)1e5+500
const ll mod = 1e9 + 9;
char s[maxn];
ll mod_pow(ll a, ll n)//快速幂取模
{
	ll ret = 1;
	while (n)
	{
		if (n & 1)ret = (ret*a) % mod;
		a = (a*a) % mod;
		n >>= 1;
	}
	return ret;
}
int main()
{
	ll n, a, b, k;
	while (~scanf("%lld%lld%lld%lld", &n, &a, &b, &k))
	{
		scanf("%s", &s);
		while (k*k < n)//先把k的长度扩展到srqt(n)左右
		{
			for (int i = 0; i < k; i++)
				s[i+k] = s[i];
			k <<= 1;
		}
		ll tmp = 0; 
		for (int i = 0; i < k; i++)
		{
			if (s[i] == '+')
				tmp = (tmp + (mod_pow(a, k - 1 - i) * mod_pow(b, i)) % mod) % mod;
			else
				tmp = (tmp - (mod_pow(a, k - 1 - i) * mod_pow(b, i)) % mod + mod) % mod;
		}
		int i,j;
		ll ans = 0;
		for (i = n - (k - 1), j = 0; i >= 0; i -= k, j += k)
			ans = (ans + (((mod_pow(a, i) * mod_pow(b, j)) % mod)*tmp) % mod) % mod;
		if (i != 0)
		{
			for (int z = i + k - 1, t = 0; z >= 0; t++, z--, j++)
				if (s[t] == '+')
					ans = (ans + (mod_pow(a, z) * mod_pow(b, j)) % mod) % mod;
				else
					ans = (ans - (mod_pow(a, z) * mod_pow(b, j)) % mod + mod) % mod;
		}
		printf("%lld\n", ans);
	}
	return 0;
}


D. Destruction of a Tree

(树)

题意:告诉你一棵树有n个结点(1~n),随后一行n个数(第i个数的值pre表示i结点的父结点,即它们之间存在一条边,当pre=0时,表示结点i为根,且只有n-1条边),问你能否依次把树的偶数度结点(包括度数为0)破坏掉,使之所有点都被破坏(每次破坏一个点,且与该点连接的边也会去掉),问你能否实现,能就输出YES并且输出破坏顺序(答案不唯一)反之输出NO.

题解:很明显,当n为偶数时,一定存在n-1条边,每次只能删去偶数度结点,那么无论怎么操作最后永远回剩2个奇数度结点,因此我们可以直接输出NO,反之进行对点的破坏操作。至于对点的操作,我们可以考虑先从树的根开始判断是否需要优先破坏,当一个结点只有奇数个子结点时,加上与它父结点连通的边度数就为偶数了,因此可以删去而不影响其他,只有他的父结点少了一个子结点。再第二遍迭代,从树根开始遍历,没有被破坏过的结点都依次破坏掉即可。因为树根开始删的话,在删去之前那些点后其他存在奇数度的结点只会受到其父结点的影响而无法删去,无边的点也在这里被删去。(具体看代码)

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define maxn (int)3e5+500
vector<int>g[maxn];
int del[maxn];
void dfs1(int u)
{
	int cnt = 0;
	for (int i = 0; i < g[u].size(); i++)
	{
		int v = g[u][i];
		dfs1(v);
		if(!del[v]) cnt++;
	}
	if (cnt % 2 == 1)
	{
		cout << u << endl;
		del[u] = 1;
	}
}
void dfs2(int u)
{
	if (!del[u]) cout << u << endl;
	for (int i = 0; i < g[u].size(); i++)
	{
		int v = g[u][i];
		dfs2(v);
	}
}
int main()
{
	int n;
	while (~scanf("%d", &n))
	{
		int rt,u;
		for (int i = 1; i <= n; i++)
		{
			g[i].clear();
			del[i] = 0;
		}
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &u);
			if (u == 0)
				rt = i;
			else
				g[u].push_back(i);
		}
		if (n % 2 == 0)
			cout << "NO" << endl;
		else
		{
			cout << "YES" << endl;
			dfs1(rt);
			dfs2(rt);
		}
	}
	return 0;
}
最后一题emmm....肝不动了
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值