【TCP】Three_C杯 解题报告

吐槽:

LYP always cheats my feelings...TAT

我还以为全部是原创题

没想到是

TC + CC + CF(Topcoder, Codechef, Codeforces) = =

故命名为3C杯= =也是吐槽初赛的TCP/IP分层吧。


【Day1】

1.币

有一排硬币堆,两个人轮流取硬币。每个选手随机取最左边或者最右边的一堆硬币。求先手期望取得的硬币数。

数据组数<= 1000, 堆数 <= 1000

显然有O(堆数^2)的DP。

然而怎么应对数据组数呢?

那么我们枚举堆数,f[i, j] 表示共i堆取到第j堆的概率。

转移方程:

f[i][1] = 1 - 0.5 * f[i - 1][1];
for (int j = 2; j < i; ++j)
f[i][j] = 1 - 0.5 * (f[i - 1][j] + f[i - 1][j - 1]);
f[i][i] = 1 - 0.5 * f[i - 1][i - 1];


2.给定一个无自环重边的无向图,求这个图的三元环1的个数以及补图2的三元环个数。

首先求出总三元环数。设每个点在原图中有k条出边,那么在补图中就有(n - k - 1)条出边

C(n, 3)为总方案,不合法的就是(sigma(i = 1 .. n) k * (n - k -1)) / 2

减去即可得出可行方案总数。

对于原图中的三元环个数,我们直接暴力枚举i的所有相邻节点,记到一个数组中,然后枚举即可。

各种奇葩优化。这个给一下code,因为实在不想解释了....code应该比我说得好。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<climits>
#include<cmath>
#define ot "%d"
#define olt "%I64d"
#define kg " "
#define ll long long
#define swap(a, b) ({int _ = (a); (a) = (b); (b) = _;})
#define maxn 100005
#define maxm 200005
#define mo 1000003
#define e6 1000000

using namespace std;

struct edge
{
	int t;
	edge *nt;
}*he[maxn], eg[maxm], *eptr;

int n, m, ans1, ans2, a[2][maxm], cnt[maxn], tail[maxn], h[1000003], t[2000005], nt[2000005], tot, tote;

void addhash(ll k)
{
	int p = k % mo, q = k / mo;
	t[++tot] = q; nt[tot] = h[p]; h[p] = tot;
}

void adde(int x, int y)
{
	*(++eptr) = (edge){y, he[x]}, he[x] = eptr; ++cnt[x];
	*(++eptr) = (edge){x, he[y]}, he[y] = eptr; ++cnt[y];
}

bool checkhash(ll k)
{
	int p = k % mo, q = k / mo;
	for (int e = h[p]; e; e = nt[e]) if (t[e] == q) return 1;
	return 0;
}

ll hashcode(int x, int y)
{
	if (x > y) swap(x, y);
	return x * (ll)1e6 + y;
}

void init()
{
	eptr = eg;
	memset(he, 0, sizeof he);
	freopen("triangle.in", "r", stdin);
	freopen("triangle.out", "w", stdout);
	scanf(ot ot, &n, &m); tot = 0;
	memset(h ,0, sizeof h);
	memset(a, 0, sizeof a);
	int x, y;
	for (int i = 1; i <= m; ++i)
		scanf(ot ot, &x, &y), addhash(hashcode(x, y)), adde(x, y);
}

int que[maxn];

int main()
{
   init();
   ll ans1 = 0, anstot = 0;
	anstot = 1ll * n * (n - 1) * (n - 2) / 3;
	for (int i = 1; i <= n; ++i)
	{
		int tail = 0;
		anstot -= 1ll * cnt[i] * (n - 1 - cnt[i]);
		for (edge *e = he[i]; e; e = e -> nt)
			if (cnt[i] < cnt[e -> t] || (cnt[i] == cnt[e -> t] && i < e -> t))
				que[++tail] = e -> t;
		for (int j = 1; j < tail; ++j)
			for (int k = j + 1; k <= tail; ++k)
				if (checkhash(hashcode(que[j], que[k]))) ++ans1;
	}
	anstot >>= 1;
	printf(olt kg olt, ans1, anstot - ans1);
   return 0;
}

3.农数

一个数 n 是农数,当且仅当对于每个质数 p ,要么 p ∤ n ,要么 p ≤ maxPrime 且存在一个正奇数 k 使得 pk| n 且 pk+1∤ n 。
求给定 maxprime ,问 1 到 Upto 里面的农数个数。Upto ≤ 1010,Maxprime ≤ 106

居然是搜索..我还以为是神级数论题。吐槽不能。

加个剪枝:搜索到质数k时,如果前面所有数的乘积now * k * k 大于n了,就在指数数组中二分出一个最大的kk,使kk * now <= n

然后直接把答案加上kk - k + 1即可。


【Day2】

1.出自Fotile的又一套NOIP模拟题。

春宵和秋锅比赛现农。
春宵说:我的农气值有:
N
t ^ k
t=0
这么大,你是不可能比我大的。
秋锅笑而不语。他在后面加了一个: (mod M) 。
然后,他问春宵:你的农气值现在是多少呢?

(好冷的题目。。)

首先有明显的O(m)做法。

然后可以通过只求m中的素数来优化。


2. 灯

路上挂着一些灯,第 i 个灯坐标为 (xi,yi) 。每个灯的照射范围为一个三角形,半张角为 zi。
你有一架飞机,但是只能飞固定高度。由于未知原因(目测秋锅农气过重),飞机只能在光下
飞行。你的任务是找一个最大的高度来从 L 飞到 R 。

二分高度然后奇葩判断。

具体看代码。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<climits>
#include<cmath>
#include<cctype>
#include<cstring>
#include<algorithm>
#define ll long long
#define ot "%d"
#define olf "%lf"
#define olt "%I64d"
#define maxn 100005

using namespace std;

struct lt
{
     double x, y, z;
}a[maxn]; 

const double eps = 0.0000000001;
int n;
double l, r;

void init()
{
	freopen("light.in", "r", stdin);
	freopen("light.out", "w", stdout);
	scanf(ot olf olf, &n, &l, &r);
	for (int i = 1; i <= n; ++i)
	scanf(olf olf olf, &a[i].x, &a[i].y, &a[i].z), a[i].z = tan(a[i].z/180 * M_PI);
}

void sort(int l, int r)
{
	int i = l, j = r;
	lt y; double x = a[(l + r) >> 1].x;
	while (i <= j)
	{
		while (a[i].x < x) ++i;
		while (a[j].x > x) --j;
		if (i <= j)
		{
			y = a[i]; a[i] = a[j]; a[j] = y;
			++i, --j;
		}
	}
	if (i < r) sort(i, r);
	if (l < j) sort(l, j);
}

bool check(double h)
{
	double t = l, lf, rt;
	for (int i = 1; i <= n; ++i) if (a[i].y >= h)
	{
		lf = a[i].x - a[i].z * (a[i].y - h);
		rt = a[i].x + a[i].z * (a[i].y - h);
		if (t >= lf && t <= rt) t = rt;
	}
	if (t >= r)
		return 1;
	else
		return 0;
}

int main()
{
	init();
	sort(1, n);
	double fl = 0, cl = 1005, md; // floor && ceil && middle
	while (cl - fl > eps)
	{
		md = (cl + fl) / 2;
		if (check(md)) fl = md; else cl = md;
	}
	printf("%.9lf", cl);
	return 0;
}



3.给定一个序列,你每次可以合并相邻两个元素,新的元素为这两个元素的和。你需要使得若干次合并之后的序列非降,求最小合并次数。

设f[i] 为前i个的最小次数,g[i]为f[i] 下的最后一个元素的值。

暴力n^2转移即可。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值