文章目录
Thinking
2020 南京 H. Harmonious Rectangle
-
思路:意识到,当矩形长边大于 9 时,0,1,2 三个数字形成的 00,01,02,10,11,12,20,21,22 一定会出现重复出现,又因为 00,11,22 在前 7 列出现一个后,下一个一出现也会一定有重复。所以当矩形长边大于 7 时一定有解。
小数据暴力搜索后打表,大数据直接快速幂计算即可。
#include<bits/stdc++.h> #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define PII pair<int, int> #define Pi 3.141592653589793 const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; const int mod = 1e9 + 7; using namespace std; int cas, n, m, a[10][10], ok; ll f[10][10], res; int power(ll a, ll b, ll mod){ //a 为对应二进制位数需要记录的值,二进制满足即乘 ll ans = 1; if(mod == 1) ans = 0; while(b > 0){ //快速幂即转为二进制按位计算 if(b % 2 == 1){ //x & 1表示: x % 2 == 1 ans *= a; ans %= mod; } a *= a; a %= mod; b >>= 1; } return ans; } // inline bool check(int n, int m){ //暴力计算后,得到结果,直接打表,此处为暴力计算代码 // rep(i, 1, n - 1) rep(j, 1, m - 1) // if((a[i][j] == a[n][j] && a[i][m] == a[n][m]) || (a[i][j] == a[i][m] && a[n][j] == a[n][m])) return 1; // return 0; // } // void solve(ll nowx, ll nowy, ll n, ll m){ // rep(color, 1, 3){ // a[nowx][nowy] = color, ok = 0; // if(check(nowx, nowy)){ // ok = 1; // res += power(3, (m * (n - nowx) + m - nowy), mod); // res %= mod; // } // int tmpx = nowx, tmpy = nowy; // if(!ok){ //若当前已填数字不能满足条件,才向下一个位置填数 // if(tmpy == m && tmpx == n) continue; // if(tmpy == m) tmpx++, tmpy = 1; // else tmpy++; // solve(tmpx, tmpy, n, m); // } // } // } int form[8][8] = {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 15, 339, 4761, 52929, 517761, 4767849}, {0, 0, 339, 16485, 518265, 14321907, 387406809, 460338013}, {0, 0, 4761, 518265, 43022385, 486780060, 429534507, 792294829}, {0, 0, 52929, 14321907, 486780060, 288599194, 130653412, 748778899}, {0, 0, 517761, 387406809, 429534507, 130653412, 246336683, 579440654}, {0, 0, 4767849, 460338013, 792294829, 748778899, 579440654, 236701429} }; int main(){ // rep(i, 1, 7){ // rep(j, 1, 7){ // if(i == 1 || j == 1) f[i][j] = 0; // else if(j < i) f[i][j] = f[j][i]; // else{ // res = 0; // memset(a, 0, sizeof(a)); // solve(1, 1, i, j); // f[i][j] = res; // } // } // } cin >> cas; while(cas--){ cin >> n >> m; if(min(n, m) == 1) cout << 0 << endl; //7 = 3 * 3 - 2 else if(max(n, m) > 7) //只有大于7才一定会重复,等于7不一定重复 cout << power(3, n * m, mod) << endl; else cout << form[n][m] << endl; } } /* 3 1 4 2 2 3 3 */
2021 昆明 Parallel Sort
-
思路:由于原数列是 1 ∼ n 1\sim n 1∼n 的一个排列,所以没有重复数字,每个数值最终也会与位置编号一一对应。当 s z sz sz 个数字构成一个位置对应的环时,发现一种最多两步便可使所有数值归位的方法。例如:(其中第一步建成逆序排列)
位置编号 1 2 3 4 5 初始值 5 1 2 3 4 第一步后 5 4 3 2 1 第二步后 1 2 3 4 5 #include<bits/stdc++.h> #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> #define Pi 3.141592653589793 const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; using namespace std; int n, a[N], circnt = 0, vis[N], now, maxn = 0, cnt2 = 0; VI cir[N]; int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin >> n; rep(i, 1, n) cin >> a[i]; memset(vis, 0, sizeof(vis)); rep(i, 1, n){ if(vis[i]) continue; now = i; //储存位置编号 circnt++; while(!vis[now]){ cir[circnt].push_back(now); vis[now] = 1; now = a[now]; //转移到下一位置 } } rep(i, 1, circnt){ int sz = (int)cir[i].size(); if(sz == 2) cnt2++; maxn = max(maxn, sz); } if(maxn == 1) cout << 0 << endl; else if(maxn == 2){ cout << 1 << endl << cnt2 << " "; rep(i, 1, circnt){ if((int)cir[i].size() == 1) continue; for(auto j : cir[i]) cout << j << " "; } } else{ cout << 2 << endl; //step1: 只变 size(长度) 大于 2 的环 int k1 = 0; rep(i, 1, circnt){ int sz = (int)cir[i].size(); if(sz == 1 || sz == 2) continue; k1 += (sz - (sz % 2 ? 0 : 2)) / 2; } cout << k1 << " "; rep(i, 1, circnt){ int sz = (int)cir[i].size(); if(sz == 1 || sz == 2) continue; rep(j, 1, sz / 2 - (sz % 2 ? 0 : 1)){ //输出加交换 cout << cir[i][j] << " " << cir[i][sz - j] << " "; swap(cir[i][j], cir[i][sz - j]); } } cout << endl; //step2: 变所有 size(长度) 大于 1 的环 int k2 = 0; rep(i, 1, circnt) k2 += (int)cir[i].size() / 2; cout << k2 << " "; rep(i, 1, circnt){ int sz = (int)cir[i].size(); if(sz == 1) continue; rep(j, 0, sz / 2 - 1) //直接输出即可 cout << cir[i][j] << " " << cir[i][sz - 1 - j] << " "; } } }
2019 哈尔滨 I. Interesting Permutation
-
思路: O ( n ) O(n) O(n)
∵ f i = max { a i , f i − 1 } , g i = min { a i , g i − 1 } ∴ h i = max { a i , f i − 1 } − min { a i , g i − 1 } 若 a i > f i , h i = a i − g i − 1 > h i − 1 若 g i − 1 < a i < f i , h i = f i − 1 − g i − 1 = h i − 1 若 a i < g i − 1 , h i = f i − 1 − a i > h i − 1 \because f_i=\max\{a_i,f_{i-1}\},g_i=\min\{a_i,g_{i-1}\} \qquad\therefore h_i=\max\{a_i,f_{i-1}\}-\min\{a_i,g_{i-1}\}\\ \begin{aligned}&若a_i>f_i,&h_i=a_i-g_{i-1}>h_{i-1}\\ &若g_{i-1}<a_i<f_i,&h_i=f_{i-1}-g_{i-1}=h_{i-1}\\ &若a_i<g_{i-1},&h_i=f_{i-1}-a_i>h_{i-1}\\ \end{aligned} ∵fi=max{ai,fi−1},gi=min{ai,gi−1}∴hi=max{ai,fi−1}−min{ai,gi−1}若ai>fi,若gi−1<ai<fi,若ai<gi−1,hi=ai−gi−1>hi−1hi=fi−1−gi−1=hi−1hi=fi−1−ai>hi−1-
因此 h 1 = 0 , h i ≥ h i − 1 h_1=0,h_i\ge h_{i-1} h1=0,hi≥hi−1 一定成立,于是无解情况需要充分讨论。
-
有解时
- 若 h i > h i − 1 h_i>h_{i-1} hi>hi−1,那么 a i a_i ai 可以为最大值或最小值两种情况,同时 g i − 1 ∼ f i − 1 g_{i-1}\sim f_{i-1} gi−1∼fi−1 变为 g i ∼ f i g_i\sim f_i gi∼fi ,其中增加 h i − h i − 1 − 1 h_i-h_{i-1}-1 hi−hi−1−1 个空位。
- 若 h i = h i − 1 h_i=h_{i-1} hi=hi−1,那么 a i a_i ai 只能在 g i − 1 ∼ f i − 1 g_{i-1}\sim f_{i-1} gi−1∼fi−1 中的空位中选择一个,并用掉一个空位。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> #define Pi 3.141592653589793 const int INF = 0x7fffffff; const int N = 2e6 + 200; const db eps = 1e-10; const ll mod = 1e9 + 7; int cas, n, ok; ll ans, h[N], midempty; int main(){ ios::sync_with_stdio(0),cin.tie(0); cin >> cas; while(cas--){ cin >> n; ok = 1; rep(i, 1, n){ cin >> h[i]; //无解情况 if(i == 1 && h[i] != 0) ok = 0; if(i > 1 && h[i] == 0) ok = 0; //a 为一个排列 if(h[i] >= n) ok = 0; if(h[i - 1] > h[i]) ok = 0; } if(!ok){ cout << 0 << endl; //不要轻易用puts("0") 不知道为什么会错!!! continue; } ans = 1, midempty = 0; rep(i, 2, n){ if(h[i] == h[i - 1]){ (ans *= midempty) %= mod; midempty--; } else{ // h[i] > h[i - 1] (ans *= 2) %= mod; midempty += h[i] - h[i - 1] - 1; } } cout << ans << endl; } } /* 3 3 0 2 2 3 0 1 2 3 0 2 3 */
-
2020 上海 D. Walker
-
思路:
-
首先
calc
这个函数绝妙,省去许多情况的讨论。 -
其次,分为三种情况:
- 一个人速度特别快,自己就走完全程即可。
- 两个人速度差距不大,但是速度慢的离自己临近端点较远,两个人只需要走到对面端点即可。
- 两个人分别负责自己的区间,争取同时完成(但不能强求),两个区间交点便应该在
p1
和p2
之间,二分查找最优答案即可。
我们一开始想到二分,但是写的复杂了许多,还没有用那个函数,越写越臃肿,思路就更乱了。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; int cas; db p1, p2, v1, v2, n, ans; db calc(db l, db r, db v){ //左边距离为l,右边距离为r时,速度为v所需要的时间 return (min(l, r) * 2.0 + max(l, r)) / v; } int main(){ cin >> cas; while(cas--){ scanf("%lf%lf%lf%lf%lf", &n, &p1, &v1, &p2, &v2); if(p1 > p2){ swap(p1, p2); swap(v1, v2); } //只有一个走 ans = min(calc(p1, n - p1, v1), calc(p2, n - p2, v2)); //交叉走 ans = min(ans, max((n - p1) / v1, p2 / v2)); //两人各走左右两边 db l = p1, r = p2, mid; while(r - l > eps){ mid = (l + r) / 2; if(calc(p1, mid - p1, v1) > calc(p2 - mid, n - p2, v2)) r = mid; else l = mid; } ans = min(ans, max(calc(p1, l - p1, v1), calc(p2 - l, n - p2, v2))); printf("%.9lf\n", ans); } } /* 2 10000.0 1.0 0.001 9999.0 0.001 4306.063 4079.874 0.607 1033.423 0.847 */
-