總的來說,這套題的難度比較接近近些年來Day1的真實難度,認為非常值得一打
GotoAndPlay
題目大意
詢問這個圖上是否存在一種跳法,能跳到這個圖上的每一個點
題目解析
犯了個低級錯誤,雙向邊忘記*2,最后兩個點RE了
因為題目告知是“跳兩次”,所以很容易想到將這個圖分成“奇數(shù)點”和“偶數(shù)點”,那么所有“奇點“和所有“偶點”是能夠獨立相互到達的。
換句話來說,只要有一個點既屬于“偶點“又屬于”奇點”,那整個圖都能相互到達了(這就是二分圖)
因此我們先在原圖做一遍DFS/BFS,標記初始標號
如果有一個點我們要對他進行標記兩種不同標號,那整個圖就完全可以到達了
如果假的一條邊,連通了兩個標記不一樣的點,那“奇點”就可以到達“偶點”了,及那整個圖就完全可以到達了
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;#define qword long long
#define seta(x, y) memset(x, y, sizeof(x))
#define forn(i, n) for (int i = 1; i <= (int)(n); ++ i)
#define rep(i, s, t) for (int i = (int)(s); i <= (int)(t); ++ i)const int maxn = 4e5 + 10;inline void File() {const string task = "GotoAndPlay";freopen((task + ".in").data(), "r", stdin);freopen((task + ".out").data(), "w", stdout);
}int n, m;
int to[maxn], head[maxn], next[maxn];
int color[maxn];
bool flag = false;int cnt = 0;inline void addEdge(int x, int y) {to[++cnt] = y;next[cnt] = head[x];head[x] = cnt;
}inline void dfs(int x, int c) {if (color[x] != -1 && color[x] != c) {flag = 1; return;}color[x] = c;for (int i = head[x]; i; i = next[i]) {if (color[to[i]] == -1) dfs(to[i], (~c) & 1);//else if (color[to[i]] == color[i]) flag = true;}
}inline void query(int x) {int a, b;scanf("%d %d", &a, &b);if (color[a] == color[b] || flag) {printf("Yes\n"); return;}printf("No\n"); return;
}int main() {//File();scanf("%d %d", &n, &m);int x, y;forn(i, m) {scanf("%d %d", &x, &y);addEdge(x, y); addEdge(y, x);}forn(i, n) color[i] = -1; // Initdfs(1, 0);int p;scanf("%d", &p);forn(i, p) query(p);return 0;
}
StopAllSounds
題目分析
一開始沒有條理的亂搞,浪費了5分鐘。。。
因為橫只為2,所以我們可以分析出當(dāng)前這只積木落下后最后的狀態(tài)。
因為發(fā)現(xiàn)當(dāng)前積木落下之后,下面所有的積木構(gòu)成已經(jīng)對后續(xù)沒有影響,這滿足了無后效性這一DP的基本條件
于是我們做一個dp,用 $ f[i][j] $ 表示現(xiàn)在最高的積木在 $ i $ 這個高度,最后一個狀態(tài)是 $ j $ (這里我用1-6表示6中最終狀態(tài))
最后進行轉(zhuǎn)移就行了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;#define qword long long
#define seta(x, y) memset(x, y, sizeof(x))
#define forn(i, n) for (int i = 1; i <= (int)(n); ++ i)
#define rep(i, s, t) for (int i = (int)(s); i <= (int)(t); ++ i)inline void File() {const string task = "StopAllSounds";freopen((task + ".in").data(), "r", stdin);freopen((task + ".out").data(), "w", stdout);
}const int mod = 1e9 + 7;int n;
long long dp[1001000][7];int main() {File();scanf("%d",&n);memset(dp, 0, sizeof(dp));forn(i, 6) dp[3][i] = 1;dp[3][4] = 0; dp[2][4] = 1;rep(i, 4, n) {dp[i][1] = (dp[i-3][1] + dp[i-2][2] + dp[i-2][3] + dp[i-3][4] + dp[i-2][5] + dp[i-3][6]) % mod;dp[i][2] = (dp[i-2][1] + dp[i-3][2] + dp[i-3][3] + dp[i-3][4] + dp[i-3][5] + dp[i-3][6]) % mod;dp[i][3] = (dp[i-3][1] + dp[i-2][2] + dp[i-2][3] + dp[i-3][4] + dp[i-2][5] + dp[i-3][6]) % mod;dp[i][4] = (dp[i-2][1] + dp[i-2][2] + dp[i-2][3] + dp[i-2][4] + dp[i-2][5] + dp[i-2][6]) % mod;dp[i][5] = (dp[i-3][1] + dp[i-3][2] + dp[i-3][3] + dp[i-3][4] + dp[i-3][5] + dp[i-3][6]) % mod;dp[i][6] = (dp[i-3][1] + dp[i-2][2] + dp[i-2][3] + dp[i-3][4] + dp[i-1][5] + dp[i-3][6]) % mod;}qword ans = 0;rep(i, 2, n) forn(j, 6)ans += dp[i][j] % mod;printf("%lld", (ans + 1) % mod);return 0;
}
UpdateAfterEvent
題目分析
這道題是這里面唯一一個達到了提高+/省選難度的題
首先思考如何將問題所求:期望的松鼠對數(shù) 轉(zhuǎn)化為我們較為熟悉和擅長入手的東西。
大家能夠發(fā)現(xiàn),盡管可能大家最先想到的計算松鼠對數(shù)的方法,是根據(jù)樹上的松鼠總數(shù) $ x $,通過 $ x * (x - 1) / 2$ 來得到。但不妨考慮一下一個更一般的方法:
枚舉一只松鼠,再枚舉另一只松鼠,如果它們在同一棵樹上則答案加一。
從這個方法中能夠得到的啟示是:松鼠對數(shù)這個量實際上是相對獨立的,即與這兩只松鼠之外的量并無直接的關(guān)系。
這樣就避免了我們陷入一味考慮如何計算“松鼠期望總數(shù)”的錯誤方向了。
相似地,當(dāng)我們計算期望時,也存在這樣一個方法:
枚舉一只松鼠 $ A $ ,再枚舉另一只松鼠 $ B $ ,考慮它們同時存在在樹i上的情況。
假設(shè)松鼠\(A\)在樹i上的概率為 $ P(A, i) $ , 松鼠B在樹i上的概率為 $ P(B, i) $
則它們同時存在于樹i上的概率為 $ P(A, i) * P(B, i) $
而這一事件構(gòu)成了“1對結(jié)束時在同一棵樹上的松鼠”
因此對答案的貢獻是 $ P(A, i) * P(B, i) * 1 $
我們只需要對于每一對松鼠枚舉一下樹 $ i $ ,然后對這些東西求和計入答案就可以了。
考慮如何計算 $ P(A,i) $ 。我們不妨假設(shè) $ f(i, j) $ 為一只松鼠從點i出發(fā),在點j停下的概率。
當(dāng) $ T = 0\(時,\) f [i][i] $ 均為1.0,每過一個單位時間時,考慮 $ f[i][j] $ 即松鼠當(dāng)前在 $ j $ 時的概率
根據(jù)題中的描述向 $ j $的相鄰點轉(zhuǎn)移。
這樣能夠得到所有 $ T \le 30 $ 的分數(shù)
觀察可知,每次的轉(zhuǎn)移事實上都是一樣的。于是我們可以使用矩陣乘法來優(yōu)化這個轉(zhuǎn)移過程。
時間復(fù)雜度 $ O(n^3logT) $ ,這部分有 $ 30 $ 的數(shù)據(jù)。
30%的部分分是給 $ n ^ 2 $ 計算期望的方法。直接枚舉兩個位置和終點的位置計算是 $ n ^ 3 $ 的,但是我們在枚舉兩個位置的時候,可以維護一下前綴和,累計的時候直接計算與前綴和的乘積。就可以將計算期望的過程優(yōu)化到 $ n ^ 2 $ 了。
轉(zhuǎn)載于:https://www.cnblogs.com/Alessandro/p/9652374.html
總結(jié)
以上是生活随笔為你收集整理的NOIP2016 “西湖边超萌小松鼠” 模拟赛的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。