2022 年多校冲刺 NOIP 联训测试 第四场
作者:互联网
甲国的军队
按照\(b[i] - a[i]\)排序即可,考场想法是\(b[i] - a[i]\)代表的是可以重复利用的,显然选大的
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c; c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c <= '9' && c >= '0')x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
const int maxn = 100005;
typedef long long ll;
int n;
struct node{int a, b;}d[maxn];
bool cmp(node x , node y){return x.b - x.a > y.b - y.a;}
void work(){
n = read();
for(int i = 1; i <= n; ++i)d[i].a = read(), d[i].b = read();
sort(d + 1, d + n + 1, cmp);
ll now = 0, ans = 0;
for(int i = 1; i <= n; ++i){
if(now < d[i].b)ans += d[i].b - now, now = d[i].b;
now -= d[i].a;
}
printf("%lld\n",ans);
}
int main(){
int T = read();
for(int i = 1; i <= T; ++i)work();
return 0;
}
虚弱
二分\(x\),为啥要三分呢?直接把绝对值拆了不就行了?
注意\(eps\)的选取
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 200005;
const double eps = 0.000000000005;
const double inf = 0x3f3f3f3f3f3f;
int n;
double a[maxn], ans = inf, ls[maxn];
int check(double mid){
for(int i = 1; i <= n; ++i)ls[i] = a[i] + mid;
double mi = ls[1], mx = ls[1];
double sx = 0, sy = 0;
for(int i = 1; i <= n; ++i){
sx += ls[i], sy += ls[i];
mi = min(mi, sx), mx = max(mx, sy);
if(sx > 0)sx = 0;
if(sy < 0)sy = 0;
}
double ab = max(abs(mx), abs(mi));
if(ab < ans)ans = ab;
if(mx < 0)return false;
if(mi > 0)return true;
if(abs(mx) > abs(mi))return true;
return false;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i)scanf("%lf",&a[i]);
double l = -10000, r = 10000;
while(r - l >= eps){
double mid = (l + r) / 2;
if(check(mid))r = mid;
else l = mid;
}
printf("%.6lf",ans);
return 0;
}
萨鲁曼的半兽人
不难发现其实是求最少将序列划分成几段,使得每一段都为回文,划分的段可以有交叉
考场就留了半小时打这题,结果可想而知
回文自动机的板子都忘了,我真是废物。
求回文可以\(hash\) + 二分,但是回文自动机显然更方便(而且回文长度在右端点)
用线段树维护\(DP\)数组,\(DP\)就很显然了,只需要在最长的回文范围内查询最小值 + 1更新当前位置答案
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100005;
char c[maxn];
int n, fail[maxn], las, len[maxn], now = 1, ch[maxn][27], rl[maxn];
struct tree{
int t[maxn << 2 | 1];
void modify(int x, int l, int r, int pos, int val){
if(l == r)return t[x] = val, void();
int mid = (l + r) >> 1;
if(pos <= mid)modify(x << 1, l, mid, pos, val);
else modify(x << 1 | 1, mid + 1, r, pos, val);
t[x] = min(t[x << 1], t[x << 1 | 1]);
}
int query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x];
int mid = (l + r) >> 1;
int ans = maxn;
if(L <= mid)ans = query(x << 1, l, mid, L, R);
if(R > mid) ans = min(ans, query(x << 1 | 1, mid + 1, r, L, R));
return ans;
}
}T;
void work(){
c[0] = '#';
// T.clear();
memset(ch, 0 ,sizeof(ch));
memset(fail, 0 ,sizeof(fail));
las = 0;now = 1;
len[1] = -1; fail[0] = 1;
for(int i = 1; i <= n; ++i){
while(c[i - len[las] - 1] != c[i])las = fail[las];
if(!ch[las][c[i] - 'a']){
len[++now] = len[las] + 2;
int j = fail[las];
while(c[i - len[j] - 1] != c[i])j = fail[j];
fail[now] = ch[j][c[i] - 'a'];
ch[las][c[i] - 'a'] = now;
}
las = ch[las][c[i] - 'a'];
rl[i] = len[las];
}
T.modify(1, 0, n, 0, -1);
for(int i = 1; i <= n; ++i)T.modify(1, 0, n, i, T.query(1, 0, n, i - rl[i], i - 1) + 1);
printf("%d\n",T.query(1, 0, n, n, n));
}
int main(){
// freopen("c2.in","r",stdin);
while(~scanf("%s",c + 1)){
n = strlen(c + 1);
work();
}
return 0;
}
序列
痛失\(70\)分!!
考场想到正解,每次处理一个区间\(solve(l, r)\)
先在与\(l\)奇偶性相同的位置找最小值,再在最小值后面奇偶性不同的位置找最小值,构成一个二元组
设两个最小值位置为\(x, y\)
接下来递归处理\(solve(l , x - 1)\) \(solve(x + 1, y - 1)\) \(solve(y + 1, r)\)
需要注意的是\(solve(x + 1, y - 1)\)必须在\(solve(l, r)\)之后,而两边的区间不必
也就是说对于部分的二元组,他们之间有严格的先后顺序,而另一部分没有
我的做法是建图拓扑
但是考场为了省事用了反向建边+反向统计答案
然而事实证明负负不得正.....
code
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
inline int read(){
int x = 0; char c; c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c <= '9' && c >= '0')x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
const int maxn = 2e5 + 55;
int q[maxn], n, mp[maxn], ls[maxn], hn, ans[maxn], pnow = 0;
struct tree{
int t[maxn << 1 | 1];
void built(int x ,int l, int r){
if(l == r){t[x] = ls[l]; return;}
int mid = (l + r) >> 1;
built(x << 1, l, mid);
built(x << 1 | 1, mid + 1, r);
t[x] = min(t[x << 1], t[x << 1 | 1]);
}
int query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x];
int mid = (l + r) >> 1, ans = maxn;
if(L <= mid)ans = query(x << 1, l, mid, L, R);
if(R > mid)ans = min(ans, query(x << 1 | 1, mid + 1, r, L, R));
return ans;
}
}T1,T2;
struct node{
int x, y;
node(){}
node(int x_,int y_){x = x_; y = y_;}
friend bool operator < (const node a, const node b){return a.x > b.x;}
};
priority_queue<node>Q;
int head[maxn], tot, rd[maxn], rm[maxn];
struct edge{int to,net;}e[maxn];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
++rd[v];
}
void solve(int l, int r, int u){
if(l >= r)return;
if(l + 1 == r){
rm[q[l]] = q[r];
if(u) add(u, q[l]);
return;
}
int m1, m2;
if(l & 1) m1 = T1.query(1, 1, hn, (l + 1) >> 1, r >> 1);
else m1 = T2.query(1, 1, hn, (l + 1) >> 1, r >> 1);
int p1 = mp[m1];
if(l & 1) m2 = T2.query(1, 1, hn, (p1 + 2) >> 1, (r + 1) >> 1);
else m2 = T1.query(1, 1, hn, (p1 + 2) >> 1, (r + 1) >> 1);
int p2 = mp[m2];
rm[m1] = m2;
if(u) add(u, m1);
solve(l, p1 - 1, u);
solve(p2 + 1, r, u);
solve(p1 + 1, p2 - 1, m1);
}
int main(){
n = read(); hn = n / 2;
for(int i = 1; i <= n; ++i)q[i] = read();
for(int i = 1; i <= n; ++i)mp[q[i]] = i;
for(int i = 1; i <= hn; ++i)ls[i] = q[i + i - 1]; T1.built(1, 1, hn);
for(int i = 1; i <= hn; ++i)ls[i] = q[i + i]; T2.built(1, 1, hn);
solve(1, n, 0);
for(int i = 1; i <= n; ++i)if(rd[i] == 0 && rm[i])Q.push(node(i, rm[i]));
while(!Q.empty()){
node now = Q.top(); Q.pop();
ans[++pnow] = now.x; ans[++pnow] = now.y;
for(int i = head[now.x]; i; i = e[i].net){
int v = e[i].to; --rd[v];
if(rd[v] == 0)Q.push(node(v, rm[v]));
}
}
for(int i = 1; i <= n; ++i)printf("%d ",ans[i]);
return 0;
}
标签:第四场,int,return,联训,maxn,2022,ans,solve,include 来源: https://www.cnblogs.com/Chencgy/p/16508741.html