1027上午考试总结

1027上午考试总结

T1

​ 题目大意:

​ 有\(n\)个位置,可以往里面填1到n的数.有\(m\)个限制条件,形如"[l,r]",表示从l到r放的数都不一样,问如何填数使整个序列字典序最小.\(n,m <= 1e5\)

​ 贪心,构造.

​ 首先将区间按左端点排序,某一些区间假设被别的区间完全包含的话,那么他肯定是没有用的,只需要考虑那些大的区间.然后每次往区间里填数都是从左往右1,2,3...这么填,可以保证字典序最小.假如两段区间有重合部分,优先把前一段区间填完后,在"回收"一些数填到下一个区间里,这个过程可以用优先队列搞一搞,我用的是时间戳,可能会被卡把.用了优先队列的话时间复杂度就是\(O(n log n)\).

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
	long long s = 0, f = 1; char ch;
	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
	return s * f;
}

const int N = 1e5 + 5;
int t, n, m;
int ans[N], vis[N];
struct line { int l, r; } a[N];

int cmp(line a, line b) {
	if(a.l == b.l) return a.r > b.r;
	return a.l < b.l;
}

int main() {
	
	for(t = read(); t ; t --) {
		n = read(); m = read();
		for(int i = 1;i <= n; i++) ans[i] = 1, vis[i] = 0;
		for(int i = 1;i <= m; i++) a[i].l = read(), a[i].r = read();
		sort(a + 1, a + m + 1, cmp);
		int now = 0;
		for(int i = 1;i <= m; i++) {
			if(now >= a[i].r) continue;
			int tmp = 1;
			if(a[i].l >= now + 1) for(int j = a[i].l;j <= a[i].r; j++) ans[j] = tmp ++;
			else {
				for(int j = a[i].l;j <= now; j++) vis[ans[j]] = i;
				for(int j = now + 1;j <= a[i].r; j++) {
					while(vis[tmp] == i) tmp ++; ans[j] = tmp; vis[tmp] = i;
				}
			}
			now = a[i].r;
		}
		for(int i = 1;i <= n; i++) printf("%d%c", ans[i], i == n ? '\n' : ' ');
	}
	
	return 0;
}

T2

​ 题目大意:

​ 有一辆货车,载货量为\(m\),有\(n\)个人,每个人有\(a[i]\)重量的物品,现在已经给定这\(n\)个人的顺序,问对于每个人,在强制放他的物品后,他前面最多有多少人可以放进自己的物品,输出剩下的最少的人数.\(n <= 1e5, a <= 1e9\)

​ 离散化 + 线段树.

​ 当强制放了第\(i\)个人后,货车的剩余容量就是\(m - a[i]\),现在要找出\(i\)前面最多能有几个人放进去,肯定是优先放物品重量小的.我们先把所有物品离散化,然后开一颗权值线段树,维护物品的个数和物品的总重量,然后在权值线段树里找个数就好了.当第\(i\)个人强制放也放不进去时记得特判输出0就好了.

#include <bits/stdc++.h>

#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)

using namespace std;

inline long long read() {
	long long s = 0, f = 1; char ch;
	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(s = ch ^ 48; isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
	return s * f;
}

const int N = 1e5 + 5;
int T, n, m;
int a[N], b[N];
struct tree { int num; long long sum; } t[N << 2];

void build(int o, int l, int r) {
	t[o].num = t[o].sum = 0;
	if(l == r) return ;
	build(ls(o), l, mid); build(rs(o), mid + 1, r);
}

int query(int o, int l, int r, int res) {
	if(res == 0) return 0;
	if(l == r) {
		if(t[o].sum <= res) return t[o].num;
		else {
			int num = res / b[l]; return min(num, t[o].num);
		}
	}
	if(t[o].sum <= res) return t[o].num;
	if(t[ls(o)].sum <= res) return t[ls(o)].num + query(rs(o), mid + 1, r, res - t[ls(o)].sum);
	else return query(ls(o), l, mid, res);
}

void insert(int o, int l, int r, int x, int val) {
	if(l == r) { t[o].num ++; t[o].sum += val; return ; }
	if(x <= mid) insert(ls(o), l, mid, x, val);
	if(x > mid) insert(rs(o), mid + 1, r, x, val);
	t[o].num = t[ls(o)].num + t[rs(o)].num;
	t[o].sum = t[ls(o)].sum + t[rs(o)].sum;
}

int main() {
	
	for(T = read(); T ; T --) {
		n = read(); m = read();
		build(1, 1, n);
		for(int i = 1;i <= n; i++) a[i] = b[i] = read();
		sort(b + 1, b + n + 1);
		int cnt = unique(b + 1, b + n + 1) - b - 1;
		for(int i = 1;i <= n; i++) {
			int tmp = m - a[i], li = lower_bound(b + 1, b + n + 1, a[i]) - b;
			if(tmp < 0) { printf("%d ", i); }
			else {
				int num = query(1, 1, n, tmp);
				printf("%d ", i - num - 1);
				insert(1, 1, n, li, a[i]);
			}
		}
		printf("\n");
	}
	
	return 0;
}

T3

​ 题目大意:

​ 给定\(n\)个数(有正有负),可以任取前几个把前几个数分成\(k\)段,使最大的那一段的总和最小,输出这个最小值.\(n <= 1e5\).

​ 二分 + DP + 离散化 + 树状数组

​ 二分的话当然是二分那个最大值, 设为\(mid\),然后跑一边DP,\(f[i]\)表示前\(i\)个数能分成的小于等于\(mid\)的段数最多有几个,那么DP转移方程就是:

\(f[i] = max(f[j] + 1) (sum[i] - sum[j] <= mid)\).\(sum\)表示前缀和.

​ 但是有一点要注意:当\(max(f[j]) = 0\)时,要判断\(sum[i]\)是否小于等于\(mid\),如果不是,那么\(f[i]\)还等于0.

​ 我们发现这个式子是\(O(N ^ 2)\)的,考虑用树状数组优化一下,怎么优化呢?

​ 我们要找的\(f[j]\)一定是满足这个条件的\(sum[j] >= sum[i] - mid\),我们开一个权值树状数组,\(t[x]\)维护比\(x\)大的最大的\(f\)值, 离散化之后查询和插入就好了.

​ 总复杂度\(O(n log^2n)\).

#include <bits/stdc++.h>

#define mid ((l + r) >> 1)

using namespace std;

inline long long read() {
	long long s = 0, f = 1; char ch;
	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
	return s * f;
}

const int N = 1e5 + 5;
const long long inf = 1e18;
int n, k, cnt;
long long ans;
int a[N], f[N], t[N];
long long b[N], sum[N];

int lowbit(int x) { return x & (-x); }

void change(int x, int val) { for(; x ; x -= lowbit(x)) t[x] = max(t[x], val); }

int query(int x) { int res = 0; for(; x < N; x += lowbit(x)) res = max(res, t[x]); return res; }

int judge(long long Mid) {
	for(int i = 1;i < N; i++) t[i] = 0;
	for(int i = 1;i <= n; i++) {
		int li = lower_bound(b + 1, b + cnt + 1, sum[i] - Mid) - b;
		int ll = lower_bound(b + 1, b + cnt + 1, sum[i]) - b;
		int tmp = query(li) + 1;
		if(tmp == 1 && sum[i] > Mid) f[i] = 0;
		else f[i] = tmp; 
		change(ll, f[i]);
		if(f[i] >= k) return 1;
	}
	return 0;
}

int main() {
	
	for(int T = read(); T ; T --) {
		n = read(); k = read();
		for(int i = 1;i <= n; i++) a[i] = read(), b[i] = sum[i] = sum[i - 1] + a[i];
		sort(b + 1, b + n + 1);
		cnt = unique(b + 1, b + n + 1) - b - 1;
		long long l = -1e10, r = 1e10;
		while(l <= r) {
			if(judge(mid)) ans = mid, r = mid - 1;
			else l = mid + 1;
		}
		printf("%lld\n", ans);
	}
	
	return 0;
}
posted @ 2020-10-29 07:08  C锥  阅读(61)  评论(0编辑  收藏  举报