Live2D

2020-11-21 考试总结

考得有些爆炸。。。

「BalticOI 2009 Day1」糖果机器

题目传送门

思路

考试的时候被绝对值困住了,只想到分类讨论。。。

我们将 \(|s_i-s_j|\) 变一下,可以发现其实就是 \(\max(s_i-s_j,s_j-s_i)\),所以可以 \(j\to i\) 的条件就是:

\[\left\{\begin{array}{l} t_i-t_j\ge s_i-s_j\\ t_i-t_j\ge s_j-s_i \end{array}\right.\]

\[\Rightarrow \left\{\begin{array}{l} t_i-s_i\ge t_j-t_j\\ t_i+s_i\ge t_j+s_j \end{array}\right. \]

然后你就发现这是一个二位偏序,你按一维排序然后用 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;
}
posted @ 2020-11-21 20:31  Dark_Romance  阅读(105)  评论(0编辑  收藏  举报