吐槽:
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转移即可。