1. 程式人生 > 其它 >$NOIP\ 2016\ Day2$ 模擬考試 題解報告

$NOIP\ 2016\ Day2$ 模擬考試 題解報告

目錄

\(NOIP\ 2016\ Day2\) 模擬考試 題解報告

得分情況

\(T1\) \(90\ Pts\)

\(T2\ 45\ Pts\) (\(Loj\) 資料)

\(T2\ 65\ Pts\) (洛谷 資料)

\(T3\ 5\ Pts\)

總分: \(140/160\ Pts\)

考試過程

\(T1\) 看一眼 組合數 數論 打表 看到楊輝三角 先寫的遞推 大概 \(70\) 然後看到 \(k\) 直接取模 能過 \(90\)\(95\) 一個小時 先扔掉 去看 \(T2\)

對著資料點一個一個寫 寫完 \(n^2\) 的又把 \(q = 0\) 的單獨拿出來 但是還是超時 大概不是很到一個小時 後面的時間死磕 \(T3\) 但並沒有拿多少分

題解

\(T1\) 組合數問題

\(O(n^2)\) 預處理 + 二維字首和優化把查詢優化到 \(O(1)\) 就能過

程式碼

/*
  Time: 6.15
  Worker: Blank_space
  Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Min(x, y) ((x) < (y) ? (x) : (y))
/*--------------------------------------標頭檔案*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定義*/
inline void File() {
	freopen("problem.in", "r", stdin);
	freopen("problem.out", "w", stdout);
}
/*----------------------------------------檔案*/
int T, k, n, m, f[2021][2021], ans[2021][2021];
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
void work() {
	n = read(); m = read();
	printf("%lld\n", ans[n][Min(n, m)]);
}
/*----------------------------------------函式*/
signed main() {
	File();
	T = read(); k = read(); f[0][0] = 1;
	for(int i = 1; i <= 2000; i++)
	{
		for(int j = 1; j <= i; j++)
		{
			f[i][i - j] = f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % k;
			ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1];
			if(!f[i][j]) ans[i][j]++;
		}
		ans[i][i + 1] = ans[i][i];
	}
	while(T--) work();
	return 0;
}

\(T2\) 蚯蚓

實際上寫了 \(70\) 分的 複雜度大概是能卡過的 但是似乎由於優先佇列的常數過大 後面的六個點就過了一個 吸氧的話分會高一些

同樣沒有吸氧的情況下 洛谷資料比 \(LOJ\) 的資料高 \(20\) 分 ...

正解

題中本身隱含單調性 先被切開的蚯蚓一定比後被切開的蚯蚓長 可以將兩堆分別儲存 每次切的時候從三堆中取最大的 切完依次放回 時間複雜度 \(O(m)\)

維護三個佇列 第一個表示原蚯蚓 第二個表示切開後較長的蚯蚓 第三個表示切開後較短的蚯蚓

先將原序列排序 省去每一秒增加每隻蚯蚓的長度的操作 轉換成在查詢砍哪隻蚯蚓時 把增加的長度算到蚯蚓的總長度上

每次在三個佇列中找最長的 切開後放到二三裡面

統計答案的時候將三個佇列合併 排序輸出

程式碼

/*
  Time: 6.15
  Worker: Blank_space
  Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------標頭檔案*/
const int B = 1e5 + 7;
const int C = 8e6 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定義*/
inline void File() {
	freopen("earthworm.in", "r", stdin);
	freopen("earthworm.out", "w", stdout);
}
/*----------------------------------------檔案*/
int n, m, q, u, v, t, Q[3][C], l[3], r[3], a[C], cnt;
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
bool cmp(int x, int y) {return x > y;}
void work(int x) {
	int id, y, z, max = 0;
	if(l[0] <= r[0] && Q[0][l[0]] + x * q > max) max = Q[0][l[0]] + x * q, id = 0;
	if(l[1] <= r[1] && Q[1][l[1]] + (x - l[1]) * q > max) max = Q[1][l[1]] + (x - l[1]) * q, id = 1;
	if(l[2] <= r[2] && Q[2][l[2]] + (x - l[2]) * q > max) max = Q[2][l[2]] + (x - l[2]) * q, id = 2;
	l[id]++; y = 1ll * max * u / v; z = max - y; if(y < z) Swap(y, z); Q[1][r[1]++] = y; Q[2][r[2]++] = z;
	if(!((x + 1) % t)) printf("%d ", max);
}
void del(int i) {
	if(i) while(l[i] <= r[i]) a[++cnt] = Q[i][l[i]] + (m - l[i]) * q, l[i]++;
	else while(l[i] <= r[i]) a[++cnt] = Q[i][l[i]] + m * q, l[i]++;
}
/*----------------------------------------函式*/
int main() {
	File();
	n = read(); m = read(); q = read(); u =  read(); v = read(); t = read();
	l[0] = r[0] = l[1] = r[1] = l[2] = r[2] = 1;
	for(int i = 1; i <= n; i++) Q[0][i] = read(); r[0] = n;
	std::sort(Q[0] + 1, Q[0] + 1 + n, cmp);
	for(int i = 1; i <= m; i++) work(i - 1);
	for(int i = 0; i <= 2; i++) del(i);
	std::sort(a + 1, a + 1 + cnt, cmp); puts("");
	for(int i = t; i <= m + n; i += t) printf("%d ", a[i]);
	return 0;
}

\(T3\) 憤怒的小鳥

\(n^2\) 列舉點對 待定係數法直接解方程 把 \(x\) 相等的判出去 把解出來 \(a > 0\) 的判出去 對於算出來的每一條拋物線 再列舉每個點 將經過的所有點都壓進去 然後 \(dp\)

狀態: \(f_S\) 表示經過點集為 \(S\) 時所用拋物線條數

轉移時外層列舉狀態 內層嘗試將一個點納入經過的集合 轉移考慮將多少個點同時納入

程式碼:

/*
  Time: 6.15
  Worker: Blank_space
  Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define emm(x, a) memset(x, a, sizeof x)
/*--------------------------------------標頭檔案*/
const double eps = 1e-9;
/*------------------------------------常量定義*/
inline void File() {
	freopen("angrybirds.in", "r", stdin);
	freopen("angrybirds.out", "w", stdout);
}
/*----------------------------------------檔案*/
int T, n, m, cnt, s[20][20], f[(1 << 18) + 2], ans;
double x[20], y[20];
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
void work() {
	n = read(); m = read(); emm(f, 63); emm(s, 0); f[0] = 0;
	for(int i = 1; i <= n; i++) scanf("%lf%lf", x + i, y + i);
	for(int i = 1; i < n; i++) for(int j = i + 1; j <= n; j++)
	{
		if(Abs(x[i] - x[j]) < eps) continue;
		double a = (y[i] / (x[i] * x[i] - x[i] * x[j]) - y[j] / (x[i] * x[j] - x[j] * x[j]));
		double b = (y[i] * x[j] * x[j] - y[j] * x[i] * x[i]) / (x[i] * x[j] * x[j] - x[j] * x[i] * x[i]);
		if(a >= 0 || Abs(a) < eps) continue;
		for(int k = 1; k <= n; k++) if(Abs(a * x[k] * x[k] + b * x[k] - y[k]) < eps) s[i][j] |= 1 << k - 1;
	}
	for(int S = 0; S < 1 << n; S++) for(int i = 1; i <= n; i++) if(!(1 << i - 1 & S))
	{
		for(int j = i; j <= n; j++)
		{
			if(i == j) f[S | (1 << i - 1)] = Min(f[S | (1 << i - 1)], f[S] + 1);
			if(Abs(x[i] - x[j]) < eps) continue;
			f[S | s[i][j]] = Min(f[S | s[i][j]], f[S] + 1);
		}
		break;
	}
	printf("%d\n", f[(1 << n) - 1]);
}
/*----------------------------------------函式*/
int main() {
//	File();
	T = read(); while(T--) work();
	return 0;
}