2021/7/15c++
水,略ide
建個 Trie 樹跑一波,不過這麼小的數據範圍貌似不用優化
不難發現就是將 \(N\) 個數分紅 \(3\) 堆,前兩堆大小相同,使得第三堆最小。ui
定義狀態 \(f[i][j][k]\) 表示前面 \(i\) 個數,第 \(1\) 堆大小爲 \(j\),第 \(2\) 堆大小爲 \(k\) ,是否可行,直接轉移,貌似能夠 bitset 優化作到 \(\mathcal{O}(\dfrac{N(\sum c)^2}{w})\)。spa
比較套路的作法,對後兩個維度做差,定義狀態 \(f[i][j]\) 表示前 \(i\) 個數,前兩堆差爲 \(j\) 時,第 \(1\) 堆能夠達到的最大重量時多少。字符串
直接轉移 \(\mathcal{O}(N\sum c)\) 已經能夠經過。get
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define pre(i,a,b) for(int i=a;i>=b;i--) #define N 100005 using namespace std; int f[N << 1], n, g[N << 1]; inline void ck(int &x,int y){if(y > x)x = y;} int main(){ scanf("%d", &n); int sum = 0; memset(f, 0xcf, sizeof(f)); int bas = N - 5; f[bas] = 0; rep(i, 1, n){ memset(g, 0xcf, sizeof(g)); int x;scanf("%d", &x); rep(j, -sum, sum) ck(g[j + bas], f[j + bas]), ck(g[j + x + bas], f[j + bas] + x), ck(g[j - x + bas], f[j + bas]); sum += x;rep(j, -sum, sum)f[j + bas] = g[j + bas]; } printf("%d\n", sum - f[bas]); return 0; }
感受是個錯題啊,像 \(\frac{1}{3}\) 這樣無限小數怎麼處理精度啊。it
考慮逆向思惟,咱們要將最終集合的數一一刪去。class
顯然最小的數只能被不大於它的數刪去,那麼咱們枚舉整個數。循環
而這個數必定是最小的數除以一個正整數倍率,咱們從小到大枚舉倍率,同時判斷在這個倍率下,區間\([A,B]\) 中是否有刪除了沒有的元素。
因爲 \(K\) 很小,因此判斷和刪除都是 \(\mathcal{O}(K)\) 級別的,因此複雜度是 \(\mathcal{O}(K^2)\) ?感受挺玄學的。
另外賽時發現 luogu 的 spj 全是鍋,直接 Hack spj 就能過。
挺有意思的題。
剛開始看很是沒有思路就先開 T6 了。
先想到多是 DAG 求最長路,發現不大可作。
觀察一下這個 \(LCS \ge Len - 1\) 的條件。兩個串長度不相等,必定一個是另外一個的前綴且長度相差 \(1\) ,不然必定兩個串最後覺得必定不相等。
因此長度相等的串且前綴相等大概是兩兩連邊,而後不等的串連邊是惟一的。感受也不是很可作。
可是把這個連邊放到 Trie 樹上亂搞,發現這就是一個走 \(Trie\) 樹的過程。咱們要找的是是一條路徑,使得路徑長度加上與路徑直接相連的邊的長度之和最大。
直接樹上 DP 便可,時間複雜度 \(\mathcal{O}(N + \sum|S|)\),須要注意的是 Trie 的空間,大概得用 vector 維護以時間換空間。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define pre(i,a,b) for(int i=a;i>=b;i--) #define N 3000005 using namespace std; int n, idx, ed[N]; vector<pair<int,int>>ch[N]; char s[N]; int get(int x,int y){ for(auto z:ch[x])if(z.first == y)return z.second; return 0; } int f[N], ans; void dfs(int x){ f[x] = ed[x]; int mx = 0, nxt = 0, sz = 0; for(auto y:ch[x]){ dfs(y.second); if(ed[y.second]){ sz++, nxt = max(nxt, f[y.second]); if(nxt > mx)swap(nxt, mx); } }ans = max(ans, nxt + mx + f[x] + max(0, sz - 2)); f[x] += mx + max(0, sz - 1); } int main(){ scanf("%d", &n); rep(i, 1, n){ scanf("%s", s + 1); int m = strlen(s + 1), cur = 0; pre(j, m, 1){ int now = get(cur, s[j] - 'a'); if(!now) ch[cur].push_back(make_pair(s[j] - 'a', ++idx)), cur = idx; else cur = now; } ed[cur] = 1; //cout<<"ww "<<cur<<endl; } dfs(0);printf("%d\n", ans); return 0; }
求機率輸出準確值還不用取模,顯然是直接枚舉全部可能(
顯然這個無限矩陣就是以給定的子矩陣爲元不斷循環,若是咱們固定一個方向,那麼以 \((i,j)\)爲起點的串,和以 \((i+an, j+bm),\ a,b\in \mathcal{Z}\) 爲起點的串必定相同。
因此咱們只用枚舉起點在 \((i, j), i\in[0, n - 1], j\in[0,m -1]\) 的串便可,而後就是求這個串的哈希值。
這個字符串開始和結束的幾個單獨拎出來,中間不斷循環。開始和結束的哈希值能夠預處理,中間的是個等比數列,能夠直接通項公式爆算。
細節比較多。注意斜的,橫的,豎的的循環節可能都不相同,斜的循環節和 \(n,m\) 的最大公約數有關,不過咱們能夠直接對整個子矩陣遍歷一遍避免過多討論。
另外建議雙哈希,賽時隨機了一個\(10^9\) 左右的質數 \(934567889\) 被卡了 \(40\) 分,而後又隨了兩個質數仍是掛了,查了半天還覺得是哪裏掛了,後來才發現原來是卡了哈希,估計出題人把 \(10^9\) 左右的模數都卡了。
後來找到 \(bas = 229, P = 10^9 + 97\) 沒有卡(
時間複雜度 \(\mathcal{O}(NM\log)\) ,實現較醜還排了序。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define pre(i,a,b) for(int i=a;i>=b;i--) #define N 505 #define P 1000000097 #define bas 229 using namespace std; int n, m, k, v[N][N], mat[N][N], idx, f[N][N], pw[N * N]; vector<int>c[N]; char s[N][N], cp[N][N]; void ins(int x,int y){ v[x][y] = ++idx;mat[x][y] = 0; c[idx].push_back(s[x][y]); while(true){ x = (x + 1) % n; y = (y + 1) % m; if(v[x][y])break ; v[x][y] = idx, mat[x][y] = c[idx].size(), c[idx].push_back(s[x][y]); } int w = c[idx].size(); //cout<<"init " <<w<<endl; rep(i, 1, w - 1)c[idx][i] = (1LL * c[idx][i - 1] * bas + c[idx][i]) % P; } void init(){ rep(i, 0, n - 1){ f[i][0] = s[i][0]; rep(j, 1, m - 1) f[i][j] = (1LL * f[i][j - 1] * bas + s[i][j]) % P; } rep(i, 0, n - 1)rep(j, 0, m - 1)if(!v[i][j])ins(i, j); } vector<int>w; int Pow(int x,int y){ int now = 1; for(;y;y >>= 1, x = 1LL * x * x % P)if(y & 1)now = 1LL * now * x % P; return now; } inline int g(int x,int y){ if(x == 1)return y + 1; return 1LL * (Pow(x, y + 1) - 1) * Pow(x - 1, P - 2) % P; } int calc(int x,int bs,int cnt){return 1LL * x * g(bs, cnt - 1) % P;} int row(int x,int y){ int res = k, pr = 0, cur = 0; if(y)pr = f[x][y - 1]; if(m - y >= res)return (f[x][y + res - 1] - 1LL * pr * pw[res] % P + P) % P; cur = (f[x][m - 1] - 1LL * pr * pw[m - y] % P + P) % P, res -= m - y; if(res <= m)return (1LL * pw[res] * cur + f[x][res - 1]) % P; int cc = res / m; res %= m; cur = 1LL * cur * Pow(bas, cc * m) % P; cur = (cur + calc(f[x][m - 1], pw[m], cc)) % P; if(!res)return cur; return (1LL * pw[res] * cur + f[x][res - 1]) % P; } int col(int x,int y){ int t = v[x][y]; y = mat[x][y], x = t; int w = c[x].size(), res = k, pr = 0, cur = 0; if(y)pr = c[x][y - 1]; if(w - y >= res)return (c[x][y + res - 1] - 1LL * pr * pw[res] % P + P) % P; cur = (c[x][w - 1] - 1LL * pr * pw[w - y] % P + P) % P, res -= w - y; if(res <= w)return (1LL * pw[res] * cur + c[x][res - 1]) % P; int cc = res / w; res %= w; cur = 1LL * cur * Pow(bas, cc * w) % P; cur = (cur + calc(c[x][w - 1], pw[w], cc)) % P; if(!res)return cur; return (1LL * pw[res] * cur + c[x][res - 1]) % P; } void rotate(){ rep(i, 0, n - 1)rep(j, 0, m - 1)cp[j][n - 1 - i] = s[i][j]; swap(n, m); rep(i, 0, n - 1)rep(j, 0, m - 1)s[i][j] = cp[i][j]; } long long gcd(long long x,long long y){return y ? gcd(y, x % y) : x;} signed main(){ scanf("%d%d%d", &n, &m, &k); pw[0] = 1;rep(i, 1, n * m)pw[i] = 1LL * pw[i - 1] * bas % P; rep(i, 0, n - 1)scanf("%s", s[i]); rep(op, 0, 3){ memset(v, 0, sizeof(v)); rep(i, 1, idx)c[i].clear(); idx = 0;init(); rep(i, 0, n - 1)rep(j, 0, m - 1) w.push_back(row(i, j)), w.push_back(col(i, j)); rotate(); } sort(w.begin(), w.end());int sum = 0, pr = ~0;long long ans = 0; for(int x : w){ if(pr != x)ans += 1LL * sum * sum, sum = 0, pr = x; sum++; }ans += 1LL * sum * sum;//cout<<endl; long long fac = 1LL * w.size() * w.size(); long long cur = gcd(ans, fac); printf("%lld/%lld\n", ans / cur, fac / cur); return 0; }
2021/7/15
水,略
建個 Trie 樹跑一波,不過這麼小的數據範圍貌似不用
不難發現就是將 \(N\) 個數分紅 \(3\) 堆,前兩堆大小相同,使得第三堆最小。
定義狀態 \(f[i][j][k]\) 表示前面 \(i\) 個數,第 \(1\) 堆大小爲 \(j\),第 \(2\) 堆大小爲 \(k\) ,是否可行,直接轉移,貌似能夠 bitset 優化作到 \(\mathcal{O}(\dfrac{N(\sum c)^2}{w})\)。
比較套路的作法,對後兩個維度做差,定義狀態 \(f[i][j]\) 表示前 \(i\) 個數,前兩堆差爲 \(j\) 時,第 \(1\) 堆能夠達到的最大重量時多少。
直接轉移 \(\mathcal{O}(N\sum c)\) 已經能夠經過。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define pre(i,a,b) for(int i=a;i>=b;i--) #define N 100005 using namespace std; int f[N << 1], n, g[N << 1]; inline void ck(int &x,int y){if(y > x)x = y;} int main(){ scanf("%d", &n); int sum = 0; memset(f, 0xcf, sizeof(f)); int bas = N - 5; f[bas] = 0; rep(i, 1, n){ memset(g, 0xcf, sizeof(g)); int x;scanf("%d", &x); rep(j, -sum, sum) ck(g[j + bas], f[j + bas]), ck(g[j + x + bas], f[j + bas] + x), ck(g[j - x + bas], f[j + bas]); sum += x;rep(j, -sum, sum)f[j + bas] = g[j + bas]; } printf("%d\n", sum - f[bas]); return 0; }
感受是個錯題啊,像 \(\frac{1}{3}\) 這樣無限小數怎麼處理精度啊。
考慮逆向思惟,咱們要將最終集合的數一一刪去。
顯然最小的數只能被不大於它的數刪去,那麼咱們枚舉整個數。
而這個數必定是最小的數除以一個正整數倍率,咱們從小到大枚舉倍率,同時判斷在這個倍率下,區間\([A,B]\) 中是否有刪除了沒有的元素。
因爲 \(K\) 很小,因此判斷和刪除都是 \(\mathcal{O}(K)\) 級別的,因此複雜度是 \(\mathcal{O}(K^2)\) ?感受挺玄學的。
另外賽時發現 luogu 的 spj 全是鍋,直接 Hack spj 就能過。
挺有意思的題。
剛開始看很是沒有思路就先開 T6 了。
先想到多是 DAG 求最長路,發現不大可作。
觀察一下這個 \(LCS \ge Len - 1\) 的條件。兩個串長度不相等,必定一個是另外一個的前綴且長度相差 \(1\) ,不然必定兩個串最後覺得必定不相等。
因此長度相等的串且前綴相等大概是兩兩連邊,而後不等的串連邊是惟一的。感受也不是很可作。
可是把這個連邊放到 Trie 樹上亂搞,發現這就是一個走 \(Trie\) 樹的過程。咱們要找的是是一條路徑,使得路徑長度加上與路徑直接相連的邊的長度之和最大。
直接樹上 DP 便可,時間複雜度 \(\mathcal{O}(N + \sum|S|)\),須要注意的是 Trie 的空間,大概得用 vector 維護以時間換空間。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define pre(i,a,b) for(int i=a;i>=b;i--) #define N 3000005 using namespace std; int n, idx, ed[N]; vector<pair<int,int>>ch[N]; char s[N]; int get(int x,int y){ for(auto z:ch[x])if(z.first == y)return z.second; return 0; } int f[N], ans; void dfs(int x){ f[x] = ed[x]; int mx = 0, nxt = 0, sz = 0; for(auto y:ch[x]){ dfs(y.second); if(ed[y.second]){ sz++, nxt = max(nxt, f[y.second]); if(nxt > mx)swap(nxt, mx); } }ans = max(ans, nxt + mx + f[x] + max(0, sz - 2)); f[x] += mx + max(0, sz - 1); } int main(){ scanf("%d", &n); rep(i, 1, n){ scanf("%s", s + 1); int m = strlen(s + 1), cur = 0; pre(j, m, 1){ int now = get(cur, s[j] - 'a'); if(!now) ch[cur].push_back(make_pair(s[j] - 'a', ++idx)), cur = idx; else cur = now; } ed[cur] = 1; //cout<<"ww "<<cur<<endl; } dfs(0);printf("%d\n", ans); return 0; }
求機率輸出準確值還不用取模,顯然是直接枚舉全部可能(
顯然這個無限矩陣就是以給定的子矩陣爲元不斷循環,若是咱們固定一個方向,那麼以 \((i,j)\)爲起點的串,和以 \((i+an, j+bm),\ a,b\in \mathcal{Z}\) 爲起點的串必定相同。
因此咱們只用枚舉起點在 \((i, j), i\in[0, n - 1], j\in[0,m -1]\) 的串便可,而後就是求這個串的哈希值。
這個字符串開始和結束的幾個單獨拎出來,中間不斷循環。開始和結束的哈希值能夠預處理,中間的是個等比數列,能夠直接通項公式爆算。
細節比較多。注意斜的,橫的,豎的的循環節可能都不相同,斜的循環節和 \(n,m\) 的最大公約數有關,不過咱們能夠直接對整個子矩陣遍歷一遍避免過多討論。
另外建議雙哈希,賽時隨機了一個\(10^9\) 左右的質數 \(934567889\) 被卡了 \(40\) 分,而後又隨了兩個質數仍是掛了,查了半天還覺得是哪裏掛了,後來才發現原來是卡了哈希,估計出題人把 \(10^9\) 左右的模數都卡了。
後來找到 \(bas = 229, P = 10^9 + 97\) 沒有卡(
時間複雜度 \(\mathcal{O}(NM\log)\) ,實現較醜還排了序。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define pre(i,a,b) for(int i=a;i>=b;i--) #define N 505 #define P 1000000097 #define bas 229 using namespace std; int n, m, k, v[N][N], mat[N][N], idx, f[N][N], pw[N * N]; vector<int>c[N]; char s[N][N], cp[N][N]; void ins(int x,int y){ v[x][y] = ++idx;mat[x][y] = 0; c[idx].push_back(s[x][y]); while(true){ x = (x + 1) % n; y = (y + 1) % m; if(v[x][y])break ; v[x][y] = idx, mat[x][y] = c[idx].size(), c[idx].push_back(s[x][y]); } int w = c[idx].size(); //cout<<"init " <<w<<endl; rep(i, 1, w - 1)c[idx][i] = (1LL * c[idx][i - 1] * bas + c[idx][i]) % P; } void init(){ rep(i, 0, n - 1){ f[i][0] = s[i][0]; rep(j, 1, m - 1) f[i][j] = (1LL * f[i][j - 1] * bas + s[i][j]) % P; } rep(i, 0, n - 1)rep(j, 0, m - 1)if(!v[i][j])ins(i, j); } vector<int>w; int Pow(int x,int y){ int now = 1; for(;y;y >>= 1, x = 1LL * x * x % P)if(y & 1)now = 1LL * now * x % P; return now; } inline int g(int x,int y){ if(x == 1)return y + 1; return 1LL * (Pow(x, y + 1) - 1) * Pow(x - 1, P - 2) % P; } int calc(int x,int bs,int cnt){return 1LL * x * g(bs, cnt - 1) % P;} int row(int x,int y){ int res = k, pr = 0, cur = 0; if(y)pr = f[x][y - 1]; if(m - y >= res)return (f[x][y + res - 1] - 1LL * pr * pw[res] % P + P) % P; cur = (f[x][m - 1] - 1LL * pr * pw[m - y] % P + P) % P, res -= m - y; if(res <= m)return (1LL * pw[res] * cur + f[x][res - 1]) % P; int cc = res / m; res %= m; cur = 1LL * cur * Pow(bas, cc * m) % P; cur = (cur + calc(f[x][m - 1], pw[m], cc)) % P; if(!res)return cur; return (1LL * pw[res] * cur + f[x][res - 1]) % P; } int col(int x,int y){ int t = v[x][y]; y = mat[x][y], x = t; int w = c[x].size(), res = k, pr = 0, cur = 0; if(y)pr = c[x][y - 1]; if(w - y >= res)return (c[x][y + res - 1] - 1LL * pr * pw[res] % P + P) % P; cur = (c[x][w - 1] - 1LL * pr * pw[w - y] % P + P) % P, res -= w - y; if(res <= w)return (1LL * pw[res] * cur + c[x][res - 1]) % P; int cc = res / w; res %= w; cur = 1LL * cur * Pow(bas, cc * w) % P; cur = (cur + calc(c[x][w - 1], pw[w], cc)) % P; if(!res)return cur; return (1LL * pw[res] * cur + c[x][res - 1]) % P; } void rotate(){ rep(i, 0, n - 1)rep(j, 0, m - 1)cp[j][n - 1 - i] = s[i][j]; swap(n, m); rep(i, 0, n - 1)rep(j, 0, m - 1)s[i][j] = cp[i][j]; } long long gcd(long long x,long long y){return y ? gcd(y, x % y) : x;} signed main(){ scanf("%d%d%d", &n, &m, &k); pw[0] = 1;rep(i, 1, n * m)pw[i] = 1LL * pw[i - 1] * bas % P; rep(i, 0, n - 1)scanf("%s", s[i]); rep(op, 0, 3){ memset(v, 0, sizeof(v)); rep(i, 1, idx)c[i].clear(); idx = 0;init(); rep(i, 0, n - 1)rep(j, 0, m - 1) w.push_back(row(i, j)), w.push_back(col(i, j)); rotate(); } sort(w.begin(), w.end());int sum = 0, pr = ~0;long long ans = 0; for(int x : w){ if(pr != x)ans += 1LL * sum * sum, sum = 0, pr = x; sum++; }ans += 1LL * sum * sum;//cout<<endl; long long fac = 1LL * w.size() * w.size(); long long cur = gcd(ans, fac); printf("%lld/%lld\n", ans / cur, fac / cur); return 0; }