2020-11-21 考试总结
考得有些爆炸。。。
「BalticOI 2009 Day1」糖果机器
思路
考试的时候被绝对值困住了,只想到分类讨论。。。
我们将 \(|s_i-s_j|\) 变一下,可以发现其实就是 \(\max(s_i-s_j,s_j-s_i)\),所以可以 \(j\to i\) 的条件就是:
然后你就发现这是一个二位偏序,你按一维排序然后用 set 维护一下即可。
时间复杂度 \(\Theta(n\log n)\)。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 100005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void Mx (T &a,T b){a = max (a,b);}
template <typename T> void Mi (T &a,T b){a = min (a,b);}
int n,tot;
struct node{
int s1,s2,ind;
bool operator < (const node &p)const{return (s1 + s2 == p.s1 + p.s2) ? (s2 - s1 < p.s2 - p.s1) : (s1 + s2 < p.s1 + p.s2);}
}t[MAXN];
bool cmp (node a,node b){return (a.s2 - a.s1 == b.s2 - b.s1) ? (a.s1 + a.s2 < b.s1 + b.s2) : (a.s2 - a.s1 < b.s2 - b.s1);}
#define IT set<node>::iterator
set <node> S;
signed main(){
read (n);
for (Int i = 1;i <= n;++ i) read (t[i].s1,t[i].s2),t[i].ind = i;
sort (t + 1,t + n + 1,cmp);
for (Int i = 1;i <= n;++ i){
IT it = S.upper_bound (t[i]);
if (it == S.begin()) t[i].ind = ++ tot;
else -- it,t[i].ind = it -> ind,S.erase (it);
S.insert (t[i]);
}
write (tot),putchar ('\n');
for (Int i = 1;i <= n;++ i) write (t[i].s1),putchar (' '),write (t[i].s2),putchar (' '),write (t[i].ind),putchar ('\n');
return 0;
}
[BalticOI 2008]手套
思路
绑点实在是太恶心了。
我们考虑哪些不能选,显然假设我们选了颜色集合 \(S\) 里面所有的手套,那么最坏情况就是我们选了 \(S\) 的补集里面所有的手套。假设两者个数分别为 \(S,T\),那么对于 \(S^{'}\le S,T^{'} \le T\),显然都不可以。也就是说原点到 \((S,T)\) 的一个矩阵里面都是不能选的点。对于 \(2^n\) 个集合都操作一遍,发现限制会长成下面这个样子:
于是我们只需要求到凸包,然后只需要求到凹下去的点的最小值就好了。如下图:
时间复杂度 \(\Theta(2^n\times n)\)。
[BalticOI 2009 Day1]甲虫
思路
水题,没有什么好说的。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 305
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void Mx (T &a,T b){a = max (a,b);}
template <typename T> void Mi (T &a,T b){a = min (a,b);}
int n,m,a[MAXN],f[MAXN][MAXN][2];
signed main(){
read (n),read (m);
for (Int i = 1;i <= n;++ i) read (a[i]);a[++ n] = 0;
sort (a + 1,a + n + 1);int st = 0;for (Int i = 1;i <= n;++ i) if (!a[i]){st = i;break;}
int ans = 0;
for (Int k = 1;k <= n;++ k){
memset (f,0xcf,sizeof (f)),f[st][st][0] = f[st][st][1] = 0;
for (Int l = n;l >= 1;-- l)
for (Int r = l;r <= min (n,l + k - 1);++ r){
int rest = k - (r - l + 1);
Mx (ans,max (f[l][r][0],f[l][r][1]));
Mx (f[l - 1][r][0],max (f[l][r][0] + m - (a[l] - a[l - 1]) * rest,f[l][r][1] + m - (a[r] - a[l - 1]) * rest));
Mx (f[l][r + 1][1],max (f[l][r][0] + m - (a[r + 1] - a[l]) * rest,f[l][r][1] + m - (a[r + 1] - a[r]) * rest));
}
}
write (ans),putchar ('\n');
return 0;
}
[BalkanOI2018]Election
思路
不难想到一种暴力,即直接暴力从左往右扫一遍然后再从右往左扫一遍。
我们定义 \(pre_i\) 表示前缀和,\(suf_i\) 表示后缀和。\(pm\) 表示 \(\min pre_i\),\(sm\) 表示 \(\min suf^{'}_i\)。这里的 \(suf^{'}_i\) 表示从左往右扫了一遍之后的后缀和。
显然答案就是 \(-pm-sm\)。问题就是如何求出 \(sm\)。
考虑一个点 \(p\) 在第一次被扫后缀增加了多少。正难则反。考虑 \(-pm\) 中没有增长 \(p\) 的后缀的次数。不难看出,答案就是:
\(-\min_{q<p} pre_q\)
于是,增长的次数就是 \(\min_{q<p} pre_q-pm\)。
于是,可以得出 \(sm=\min_{p}\{suf_p+\min_{q<p} pre_q-pm\}\)
于是 \(-pm-sm=-\min_p\{suf_p+\min_{q<p}pre_q\}\)
不难看出这个就是区间最大子段和减去区间总和。求区间最大子段和可以线段树上查询。
时间复杂度 \(\Theta(n\log n)\)
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 500005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int n,a[MAXN],s[MAXN];
struct node{
int sum,lmax,rmax,maxn;
node operator + (const node &p)const{return node{sum + p.sum,max (lmax,sum + p.lmax),max (p.rmax,p.sum + rmax),max (max (maxn,p.maxn),rmax + p.lmax)};}
}tree[MAXN << 2];
void build (int x,int l,int r){
if (l == r) return tree[x] = node {a[l],a[l],a[l],a[l]},void ();
int mid = (l + r) >> 1;
build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r);
tree[x] = tree[x << 1] + tree[x << 1 | 1];
}
node query (int x,int l,int r,int ql,int qr){
if (l >= ql && r <= qr) return tree[x];
int mid = (l + r) >> 1;
if (qr <= mid) return query (x << 1,l,mid,ql,qr);
else if (ql > mid) return query (x << 1 | 1,mid + 1,r,ql,qr);
else return query (x << 1,l,mid,ql,qr) + query (x << 1 | 1,mid + 1,r,ql,qr);
}
signed main(){
read (n);
char c = getchar();
while (c != 'C' && c != 'T') c = getchar();
for (Int i = 1;i <= n;++ i)
a[i] = c == 'C' ? 1 : -1,
s[i] = s[i - 1] + a[i],c = getchar();
build (1,1,n);
int q;read (q);
while (q --> 0){
int l,r;read (l,r);
write (max (0,query (1,1,n,l,r).maxn) - (s[r] - s[l - 1])),putchar ('\n');
}
return 0;
}