codeforces EDU 15 (A,B,C,D,E)题解

16 篇文章 0 订阅
4 篇文章 0 订阅

为啥没F题?

有打野说是可持久化treap,我不会啊……还有讨论别的做法的,但是我都不会……也看不懂。


赛场上,最终AC也只有2个人,以后再补这道题。


AMaximum Increase题:

题目大意:

给定一串数字,问其中连续的一段,单调递增的序列,最长的长度是多长。  是子数组,不是子序列哦!

f[i] = {f[j] + 1 | a[i]>a[j]} 否则为f[i]=1

转移即可。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;


typedef long long LL;
const LL mod = 1000000007LL;
LL powMod( LL a , LL b , LL p = mod )//a^b % p
{
    LL r = 1 ;
    a %= p ;
    while( b )
    {
        if( b&1 ) r = r*a%p ;
        b >>= 1 ;
        a = a*a%p ;
    }
    return r ;
}


int main()
{
	int n;
	int ans = 1;
	scanf("%d", &n);
	int now,pre;
	scanf("%d", &pre);
	int now_length=1;
	for (int i = 2; i <= n; ++ i)
	{
		scanf("%d", &now);
		if (now > pre)
		{
			++ now_length;
		}
		else now_length = 1;
		swap(now, pre);
		ans = max(ans, now_length);
	}
	printf("%d\n",ans);
	return 0;
}


B题Powers of Two:题目大意


给一个 n,后面n个数字分别为a1,a2,a3...an


问,有多少个形如ai+aj = 2^x的式子。i,j不同


预处理所有2^x,预处理对于ai而言,他需要哪些数字可以组合出2^n


比如对于3,可以和1(组合出4),5(组合出8),和13(组合出16)…… 因为ai最大为1e9,所以能组合出的2^x的x不会太大。


然后用一个map,保存每个数字出现了多少次。 对于3的时候,直接查询有几个1,几个5,几个13…… 然后把答案累加即可。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
typedef long long LL;

LL ans = 0;
int n;
map<LL, LL>mp;

void ins(LL k)
{
	if (mp.find(k) == mp.end())
	{
		mp[k] = 1;
	}else mp[k] ++;
}

const int maxn = 100010;

LL a[35];
LL b[maxn];

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i)
	{
		int t;
		scanf("%d", &t);
		ins(t);
		b[i] = t;
	}
	a[0] = 1;
	for (int i = 1; i <= 32; ++ i)	a[i] = a[i - 1] * 2;
//	for (int i = 1; i <= 32; ++ i)	cout<< a[i] <<endl;
	for (int i = 1; i <= n; ++ i)
	{
		for (int j = 1; j <= 32; ++ j)
		{
			LL tmp = a[j] - b[i];
			if (mp.find(tmp) == mp.end())	continue;
			ans += (mp[tmp]);
			if (tmp == b[i])	ans--;
		}
	}
	cout << ans / 2 << endl;
	return 0;
}




C题Cellular Network:题目大意:


一条直线上,有若干个村庄,有若干个基站。


基站覆盖半径为k的时候,如果村庄距离基站的距离小于等于k的时候,则这个村庄被基站所覆盖。


为最小的k,可以让所有的村庄被覆盖。


二分答案,直接暴力判断即可。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;


typedef long long LL;
const LL mod = 1000000007LL;
LL powMod( LL a , LL b , LL p = mod )//a^b % p
{
    LL r = 1 ;
    a %= p ;
    while( b )
    {
        if( b&1 ) r = r*a%p ;
        b >>= 1 ;
        a = a*a%p ;
    }
    return r ;
}


LL d, k, a, b, t;


LL solve()
{
	if (k >=d)	return a * d;
	if (k * a + t - k * b >= 0)
	{
		return d * b - k * b + k * a;
	}
	LL x = d / k - 1;
	return d * b - k * b + k * a + x * (k * a + t - k * b);
}

LL get() //车结尾 
{
	if (k >= d)	return a * d;
	LL x = d / k;
	LL s = d % k;
	return (a * k + t) * x + s * a;
}

int main()
{
	cin >> d >> k >> a >> b >> t;
	LL ans1 = solve(); //步行结尾
	LL ans2 = get(); // 车结尾
	cout << min(ans1, ans2) << endl;
	return 0;
}



D题Road to Post Office:题目大意


从A地出发到B地,

有一个交通工具是车,车走K米,就会坏。 坏了如果修的话,要T时间。可以不修,直接下车往目的地走。步行的速度小于车的速度,问最短需要的时间。


设车开了X个K米(要修理)外加一个车程(不修理,车坏了下车直接走向目的地),列出一个方程,发现这是一个一次方程……一次方程,就是X要不取最大的,要不取0。


方程自己求好了。。我不想打了。。。


几个会被X的要点:

1、可以车直接开到终点,不需要下车也不需要步行,更不要修车

2、一直开车,不下车步行,直到到终点

3、开车,修车,步行

4、开车,不修车,步行

其实程序没那么复杂啦……

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;


typedef long long LL;
const LL mod = 1000000007LL;
LL powMod( LL a , LL b , LL p = mod )//a^b % p
{
    LL r = 1 ;
    a %= p ;
    while( b )
    {
        if( b&1 ) r = r*a%p ;
        b >>= 1 ;
        a = a*a%p ;
    }
    return r ;
}


LL d, k, a, b, t;


LL solve()
{
	if (k >=d)	return a * d;
	if (k * a + t - k * b >= 0)
	{
		return d * b - k * b + k * a;
	}
	LL x = d / k - 1;
	return d * b - k * b + k * a + x * (k * a + t - k * b);
}

LL get() //车结尾 
{
	if (k >= d)	return a * d;
	LL x = d / k;
	LL s = d % k;
	return (a * k + t) * x + s * a;
}

int main()
{
	cin >> d >> k >> a >> b >> t;
	LL ans1 = solve(); //步行结尾
	LL ans2 = get(); // 车结尾
	cout << min(ans1, ans2) << endl;
	return 0;
}


F题Analysis of Pathes in Functional Graph:题目大意

给你一个图,这个图有个特点,每个点出度为1.


每个边有权重为wi,问你,从一个点出发,走K步,所经过的所有点的权重和是多少,经过所有点权重最小的点是多少。 这个询问,从点1一直问到点n。


倍增算法,类似ST算法。 不懂ST算法的最好去学一下 ST算法先。


这道题AC后还有3秒比赛结束……要是错了我也没法改了……还好最终是AC的。


g[i][j]表示从i出发,走2^j步,所停留的点

f[i][j]表示从i出发,走2^j步,所经过的点的权重和

m[i][j]表示表示从i出发,走2^j步,所经过的点的权重的最小值

next[i]表示i的后续节点是什么

w[i]表示权重

用形如st算法的方法转移即可。

g[i][j] = g[g[i][j-1]][j-1]

f[i][j] = f[i][j-1] + f[g[i][j-1]][j-1]

m[i][j]形式类似


#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;


typedef long long LL;

const int maxn = 100010;

int n;
LL k;
int nextttt[maxn];
LL w[maxn];

void init()
{
	scanf("%d%I64d", &n, &k);
	for (int i = 0; i != n; ++ i)
		scanf("%d", &nextttt[i]);
	for (int i = 0; i != n; ++ i)
		scanf("%I64d", &w[i]);
}

LL f[maxn][50], g[maxn][50], m[maxn][50];
vector<int>sb;

void check(int k)
{
	LL xiao = 10000000000000LL;
	LL sum = 0;
	int now = k;
	for (int i = 0; i != sb.size(); ++ i)
	{
		int p = sb[i];
		xiao = min(xiao, m[now][p]);
		sum += f[now][p];
		now = g[now][p];
	}
	printf("%I64d %I64d\n", sum, xiao);
}

void doit()
{
	memset(f, 0, sizeof(f));
	memset(g, 0, sizeof(g));
	sb.clear();
	for (int i = 0; i != n; ++ i)
	{
		f[i][0] = w[i];
		g[i][0] = nextttt[i];
		m[i][0] = w[i];
	}
	for (int j = 1; j <= 45;++j)
		for (int i = 0; i != n; ++ i)
		{
			f[i][j] = f[i][ j - 1] + f[g[i][j - 1]][j - 1];
			g[i][j] = g[g[i][j-1]][j - 1];
			m[i][j] = min(m[i][j - 1] , m[g[i][j - 1]][j - 1]);
			//cout<<f[i][j]<<endl;
		}

	for (int i = 0; i <= 45; ++ i)
		if ((k >> i) & 1)	
		{
			sb.push_back(i);
			//cout<<"?"<<" "<<i<<endl;
		}
	
	//for (int i = 0; i != sb.size(); ++ i)	cout <<"@"<< i << endl;
	//return;

	for (int i = 0; i != n; ++ i)
	{
		check(i);
	}


}

int main()
{
	init();
	doit();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值