NOIP多校联考4
B.虚弱(weakness)
当x增加的时候,答案应该是一个先减小再增大的过程,也就是说答案关于x是一个单峰函数,对于单峰函数我们可以用三分法求得极值。
work函数找到最大的修改后的前缀和和最小的修改后的前缀和,相减就是x为特定值时的答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long double ll; const int maxn = 2e5 + 2; const ll mod = 998244353; const int INF = 0x7ffffff; ll a[maxn], ans, sum[maxn]; int n; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } bool check() { int flag = a[1]; for(int i=1; i<=n; i++) { if(flag != a[i]) return false; } return true; } ll work(ll sub) { ll final_max = 0, final_min = 0; ll now_max = 0, now_min = 0; for(int i=1; i<=n; i++) { now_max += a[i] - sub; now_min += a[i] - sub; if(now_max > final_max) final_max = now_max; if(now_min < final_min) final_min = now_min; if(now_max < 0) now_max = 0; if(now_min > 0) now_min = 0; } return final_max > -final_min ? final_max : -final_min; } int main() { freopen("weakness2.in", "r", stdin); n = read(); for(int i=1; i<=n; i++) { scanf("%Lf", &a[i]); } if(check()) { printf("%.6lf", 0.0); exit(0); } ll l = -10000, r = 10000; ll vall = work(l), valr = work(r); //printf("%.6lf %.6lf\n", vall, valr); while(r - l > 1e-13) { ll mid1 = l + (r - l) / 3; ll mid2 = r - (r - l) / 3; //printf("%.2lf %.2lf\n", mid1, mid2); ll val1 = work(mid1); ll val2 = work(mid2); if(vall >= val1) { if(val1 >= val2) { if(val2 >= valr) { l = mid2; vall = val2; } else { l = mid1; vall = val1; } } else { r = mid2; valr = val2; } } else { r = mid1; valr = val1; } } printf("%.6Lf", vall); return 0; }
C.萨鲁曼的半兽人(orc)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const ll mod = 998244353; const int INF = 0x7ffffff; int v[30], c, n; char s[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { while(scanf("%s", s+1)!=EOF) { memset(v, 0, sizeof(v)); c = 0; n = strlen(s+1); for(int i=1; i<=n; i++) { if(v[s[i]-'a']) { c = i - v[s[i]-'a']; break; } v[s[i]-'a'] = i; } if(c == 0 || c > 2) { printf("%d\n", n-1); } else if(c == 1) { printf("%d\n", n-2); } else if(c == 2) { printf("%d\n", n-3); } } return 0; }
这道题的原型是BZOJ3790神奇项链
https://blog.csdn.net/try__jhf/article/details/82973661题解
https://blog.csdn.net/rwbyblake/article/details/107991949manacher
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ll; const int maxn = 2e5 + 2; const ll mod = 998244353; const int INF = 0x7ffffff; int n, len, p[maxn], ans; char s[maxn], st[100005]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct data { int L, R; data(int x=0, int y=0) {L = x-y+1; R = x+y-1;} bool operator < (const data &b) const { if(L == b.L) return R < b.R; return L < b.L; } }a[maxn]; void work() { int mx = 0, id = 0; for(int i=1; i<=n; i++) { if(mx > i) { p[i] = min(p[2*id-i], mx-i); //printf("cmp : %d %d\n", p[2*id-i], mx-i); } else p[i] = 1; while(i-p[i] >= 1 && i+p[i]<=n && s[i-p[i]]==s[i+p[i]]) { p[i]++; } if(mx < i+p[i]) { mx = i+p[i]; id = i; } a[i] = data(i, p[i]); //printf("p[%d] = %d\n", i, p[i]); //printf("a[%d].L = %d a[%d].R = %d\n", i, a[i].L, i, a[i].R); } } int main() { freopen("orc2.in", "r", stdin); while(scanf("%s", st+1)!=EOF) { len = strlen(st+1); n = 1; s[1] = '#'; ans = -1; for(int i=1; i<=len; i++) { s[++n] = st[i]; s[++n] = '#'; } work(); sort(a+1, a+1+n); for(int i=1,j=1,mx=0; i<=n; ) { while(j<=n && a[j].L<=i) { mx = max(mx, a[j++].R); } i = mx + 1; ans++; } printf("%d\n", ans); } return 0; }
D.序列(seq)
这道题的重点就是:明白最后剩下的数会把区间分成三部分,然后把大区间拆成小区间。不过用优先队列来处理拼接的顺序,用RMQ来找区间最值,把奇数位置和偶数位置分开顺便填上1e6……细节还是很有趣的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 2; const ll mod = 998244353; const int INF = 0x7ffffff; struct node { int l, r, lm, rm; bool operator < (const node & T) const { return l > T.l; } }po; priority_queue<node> s;//从小到大排个序 int ji[maxn][22], ou[maxn][22]; int n, st[maxn], top, pos[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } //倍增维护区间最小值,或者说称为ST表?我还是习惯叫它RMQ…… void init() { for(int j=1; j<=20; j++) { for(int i=1; i<=n; i++) { if(i+(1<<j)-1>n) continue; ji[i][j] = min(ji[i][j-1], ji[i+(1<<(j-1))][j-1]); ou[i][j] = min(ou[i][j-1], ou[i+(1<<(j-1))][j-1]); } } } inline int Get_1(int L, int R) { int len = log2(R-L+1);//用一个while循环效果一样 return min(ji[L][len], ji[R-(1<<len)+1][len]); } inline int Get_0(int L, int R) { int len = log2(R-L+1); return min(ou[L][len], ou[R-(1<<len)+1][len]); } inline void putin(int L, int R) { //printf("L = %d R = %d\n", L, R); node poo; if(L >= R) return;//这就不合法了(不用选了) if(L == R-1)//这就没得选了 { if(L & 1) { poo.l = ji[L][0]; poo.r = ou[L+1][0]; } else { poo.l = ou[L][0], poo.r = ji[L+1][0]; } poo.lm = L; poo.rm = R; s.push(poo); return; } if(L & 1) { poo.l = Get_1(L, R); poo.r = Get_0(pos[poo.l]+1, R); } else { poo.l = Get_0(L, R); poo.r = Get_1(pos[poo.l]+1, R); } poo.lm = L, poo.rm = R; s.push(poo); } int main() { n = read(); bool is = 1; for(int i=1; i<=n; i++) { if(i & 1) { ji[i][0] = read(); ji[i+1][0] = 1e6; pos[ji[i][0]] = i; } else { ou[i][0] = read(); ou[i-1][0] = 1e6; pos[ou[i][0]] = i; } if(ji[i][0] != i && ou[i][0] != i) is = 0; } if(is) { for(int i=1; i<=n; i++) { printf("%d ", i); } exit(0); } //printf("111"); init(); //printf("111"); po.l = Get_1(1, n); //为什么新串中第一个一定要取奇数位?(我考试时就是因为没想到这个问题连暴搜都不会了) //因为要让剩下的数成为前两个,取剩下的数时一定不能跨过最后取的数,相当于划两道分界线 //不是吧又是三分? po.r = Get_0(pos[po.l]+1, n);//然后从剩下的偶数位里找到一个搭配就好 po.lm = 1; po.rm = n;//就是左右边界 s.push(po); while(!s.empty()) { po = s.top(); s.pop(); st[++top] = po.l, st[++top] = po.r;//好嘛,刚出一个又进一个 putin(po.lm, pos[po.l]-1); putin(pos[po.l]+1, pos[po.r]-1); putin(pos[po.r]+1, po.rm); } for(int i=1; i<=top; i++) { printf("%d ", st[i]); } return 0; }
时光花火,水月星辰