2021“MINIEYE杯”中国大学生算法设计超级联赛(1)个人解题报告
文章目錄
- HDU6950 Mod, Or and Everything
- HDU6954 Minimum spanning tree
- HDU6958 KD-Graph
- HDU6957 Maximal submatrix
- HDU6955 Xor sum
中超聯賽
CSL中超可沒巨佬們訓練強度大
“什么是BSGS、莫隊、KD tree”
我看我是完全不懂噢
HDU6950 Mod, Or and Everything
題意
求(nmod1)∣(nmod2)∣...∣(nmod(n?1))(n\ mod\ 1)|(n\ mod \ 2)|...|(n\ mod\ (n-1))(n?mod?1)∣(n?mod?2)∣...∣(n?mod?(n?1))的值
分析
打比賽時,
隊友:經過化簡可以得到答案為2k?12^k-12k?1
我:觀察樣例可以得到答案為2k?12^k-12k?1
QAQ
而實際上std的思路:nmodi≤?n2??1n\ mod\ i \leq \lceil \frac{n}{2}\rceil-1n?mod?i≤?2n???1,所以nmod(n?i)=in\ mod\ (n-i)=in?mod?(n?i)=i
所以這個和值就是000到?n2??1\lceil \frac{n}{2}\rceil-1?2n???1的所有整數的和. 求出來是2k?12^k-12k?1,這里的kkk是某個帶nnn的式子,懶得認真推的話可以根據樣例猜出,是令n=2xn=2^xn=2x,這個xxx向下取整再減1得到kkk. 但如果xxx本來就是整數,那么xxx還要再減去1.(總之看代碼吧,或者去看std代碼)
代碼
#include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll; //const ll linf = 9223372036854775807LL; // const ll linf = 1e18; using namespace std;signed main() {DDLC_ESCAPE_PLAN_FAILED;int t;cin >> t;while(t--){int n;cin >> n;int x = 0, res = n;while(res){res >>= 1LL;x++;}if(n == 1LL){cout << 0 << endl;continue; }if(n == (1LL << (x - 1LL))) x--;cout << (1LL << (x - 1LL)) - 1LL << endl;}return 0; }HDU6954 Minimum spanning tree
題意
有一個n?1n-1n?1個節點的帶權完全圖,節點標記為2,3,...,n2,3,...,n2,3,...,n. 完全圖中從點iii到點jjj的邊的權值大小為lcm(i,j)lcm(i,j)lcm(i,j),請你求出這個圖的最小生成樹。n≤10000000n\leq 10000000n≤10000000.
分析
容易知道,對任何一個邊(i,j)(i,j)(i,j),這個邊的權值不會小于max(i,j)max(i,j)max(i,j).
那么對于一個合數,它連向自己的因子即可,這時候邊的權值就是這個合數。由于因子一定比自己小,所以在2,3,..i2,3,..i2,3,..i中一定存在。
而對于一個質數xxx,和任意一點iii相連,都有lcm(x,i)=x?ilcm(x,i)=x·ilcm(x,i)=x?i. 故令iii最小,取2即可保證邊權最小。
分析完畢。找出質數,然后分類處理即可。線性篩處理之
其他
首先看到題:哇,1e81e81e8的范圍,這怎么做。這算法都給限制到O(n)O(n)O(n)了,難不成是有什么公式?
思考一會之后:(其實已經得到正解)用線性篩試一波,自己調試1.5秒,完蛋
嘗試各種卡常之后:優化到1.1秒了,實在頂不住了,要不直接交?
交上去之后:臥槽,MLE,那我不求MSTMSTMST前綴和了吧,直接算,但是這樣更容易超時了,糾結
再交:居然700多ms過了,杭電評測機強啊
打完比賽后,才發現,原來數據規模看錯了,是1e71e71e7,根本不需要卡()
代碼
#include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll; //const ll linf = 9223372036854775807LL; // const ll linf = 1e18; using namespace std; const int maxn = 1e7 + 10; // 比賽的時候看成1e8,導致多了一次MLE bool p[maxn]; int pr[maxn]; int pre[maxn]; void Prime(){int cnt = 0;mem(p);p[0] = p[1] = 1;for(int i = 2; i < maxn; ++i){if(!p[i]) pr[cnt++] = i;for(int j = 0; j < cnt && pr[j] * i < maxn; ++j){p[pr[j] * i] = 1;if(i % pr[j] == 0) break;}}pre[0] = pre[1] = pre[2] = 0;for(int i = 3; i < maxn; ++i){if(!p[i]) pre[i] = pre[i - 1] + 2 * i;else pre[i] = pre[i - 1] + i;} } signed main() {DDLC_ESCAPE_PLAN_FAILED;int t;cin >> t;Prime();while(t--){int n;cin >> n;cout << pre[n] << endl;}return 0; }HDU6958 KD-Graph
看到過題數不多以及題面比較奇怪就沒做了,以后一定不能盲目看榜,以及耐心讀懂題
題意
有一個nnn點mmm邊的圖(n≤100000n\leq100000n≤100000, m≤500000m\leq500000m≤500000),你需要將其分割成恰好KKK組,滿足:
現在給定圖和KKK,問你是否找得到一個滿足條件的DDD,如果找得到,輸出最小的DDD. 否則輸出?1-1?1.
分析
容易知道,相同權值的邊要么不在任何組內部,要么全都在同一組點里面。假如相同權值的邊,有的在組內,有的在組外的話,這個DDD值肯定只會更大不會更小。
那么貪心思路顯而易見了,將所有邊按權值升序排序,使用并查集,從左往右,將所有權值相同的邊的點合并。然后統計并查集數量,更新答案。也就是在保證“相同權值的邊要么不在任何組內部,要么全都在同一組點里面”這個性質的前提下尋找能否分為KKK組。而要使DDD盡可能小,應讓處于組內的邊盡可能小,所以使用升序。
代碼
#include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define pii pair<int, int> #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) // #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll; //const ll linf = 9223372036854775807LL; // const ll linf = 1e18; using namespace std; const int maxn= 100000 + 5; int fa[maxn]; struct edge{int l, r, val;bool operator < (const edge& b)const{return val < b.val;} }; vector<edge> e; inline int find(int x) {if(fa[x] == x) return x;return fa[x] = find(fa[x]); } bool join(int x, int y) {int ans = 0;int fx = find(x), fy = find(y);if(fx == fy) return 0;fa[fy] = fx;return 1; } signed main() {DDLC_ESCAPE_PLAN_FAILED;int t;cin >> t;while(t--){e.clear();int n, m, k;cin >> n >> m >> k;fors(i, 1, n) fa[i] = i;int u, v, c;fors(i, 1, m){cin >> u >> v >> c;e.pb({u, v, c});}sort(e.begin(), e.end());int now = n; // 并查集數量,初始為nbool flag = 0;int ans = 0;for(int i = 0; i < m; ++i){if(i == 0 || e[i].val != e[i - 1].val){if(now == k) break;}if(!join(e[i].l, e[i].r)) continue;now--;ans = e[i].val;}cout << (now == k ? ans : -1) << endl;}return 0; }HDU6957 Maximal submatrix
題意
給出一個矩陣,要你找一個最大的子矩陣,要求這個子矩陣每一列從上往下都是單調不下降序列。輸出最大子矩陣的面積
分析
懸線法,將每個不比上面的元素小的元素標記,然后用這些標記過的元素組成子矩陣。接下來使用懸線dp即可。需要注意的是,子矩陣的最上面一行是可以不被標記的。
代碼
(比賽時自己凹的二維dp一直超時,應該是復雜度超過O(n2)O(n^2)O(n2)了)
第一次接觸到懸線法,代碼可能相對繁瑣
#include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) // #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll; //const ll linf = 9223372036854775807LL; // const ll linf = 1e18; using namespace std; const int maxn = 2005; bool v[maxn][maxn]; int l[maxn][maxn]; int r[maxn][maxn]; int u[maxn][maxn]; signed main() {DDLC_ESCAPE_PLAN_FAILED;int t;cin >> t;while(t--){mem(v), mem(l), mem(r), mem(u);int n, m;cin >> n >> m;fors(i, 1, n){fors(j, 1, m){cin >> u[i][j];l[i][j] = j;r[i][j] = j;}}fors(i, 1, n){fors(j, 1, m){if(i == 1 || u[i][j] >= u[i - 1][j]){v[i][j] = 1;}}}fors(i, 1, n){fors(j, 2, m){if(v[i][j] && v[i][j - 1]) l[i][j] = l[i][j - 1];}for(int j = m - 1; j >= 1; --j){if(v[i][j] && v[i][j + 1]) r[i][j] = r[i][j + 1];}}mem(u);fors(i, 1, n){fors(j, 1, m) u[i][j] = 1;}int ans = 0;fors(i, 1, n){fors(j, 1, m){if(v[i][j] && v[i - 1][j]){l[i][j] = max(l[i - 1][j], l[i][j]);}if(v[i][j]) u[i][j] = u[i - 1][j] + 1;}for(int j = m; j >= 1; --j){if(v[i][j] && v[i - 1][j]){r[i][j] = min(r[i - 1][j], r[i][j]);}}}fors(i, 1, n){fors(j, 1, m){ans = max(ans, u[i][j] * (r[i][j] - l[i][j] + 1));}}cout << ans << endl;}return 0; }HDU6955 Xor sum
題意
給一個整數數組,你需要找到最短的區間,其按位異或和不小于kkk. 如果有多個,找出左端點最左的
分析
先轉化一下。異或運算里,任意xxx的逆元是xxx本身,故對于前綴和pre[i]pre[i]pre[i],pre[j]pre[j]pre[j],iii到jjj的異或和可以表示為pre[i]pre[i]pre[i]^pre[j]pre[j]pre[j].
這之后,直接存異或和,問題轉化為:找到兩個相距最近的i,ji,ji,j,其異或和不小于kkk.
枚舉右端點,然后可能的左端點用TrieTrieTrie樹維護,從左到右找,每次找到一個可能的編號就覆蓋一下,這樣能保證最后找到的是最右邊的。不過也可以直接保存最右邊的節點。
以后千萬不要看著std調試代碼,調著調著就調得跟std一模一樣了
不過說白了也是之前從來沒用過trie吧orz,根本不熟
代碼
/*** @file :vsDebug.cpp* @brief :* @date :2021-07-21* @Motto :Love Sakurai Yamauchi Forever*/ #include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) // #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll; //const ll linf = 9223372036854775807LL; // const ll linf = 1e18; using namespace std; const int maxn = 1e5 + 10; const int mnx = (1LL << 24) + 10; int p[mnx][2], res[mnx], a[maxn]; signed main() {DDLC_ESCAPE_PLAN_FAILED;int t;cin >> t;while(t--){int n, k;cin >> n >> k;fors(i, 1, n){cin >> a[i];a[i] ^= a[i - 1]; // 只存前綴和}int l = -1, r = n, ans = 1;res[1] = -1;p[1][0] = p[1][1] = 0; // initfors(i, 0, n){int x = 1;int tmp = -1;for(int j = 29; j >= 0; --j){if(!x) break;int u = (a[i] >> j) & 1; // 從上往下第j位if(!((k >> j) & 1)){if(p[x][u ^ 1]){tmp = max(tmp, res[p[x][u ^ 1]]);}x = p[x][u];}else x = p[x][u ^ 1];}if(x) tmp = max(tmp, res[x]);if(tmp >= 0 && i - tmp < r - l) l = tmp, r = i;x = 1;for(int j = 29; j >= 0; --j){int u = (a[i] >> j) & 1;if(!p[x][u]){ans++;p[x][u] = ans, res[ans] = -1;p[ans][0] = p[ans][1] = 0;}x = p[x][u];res[x] = max(res[x], i);}}if(l >= 0) cout << l + 1 << ' ' << r << endl;else cout << -1 << endl;}return 0; }總結
以上是生活随笔為你收集整理的2021“MINIEYE杯”中国大学生算法设计超级联赛(1)个人解题报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle数据库打补丁方法
- 下一篇: 【bzoj3698】XWW的难题 有上