1027下午考试总结

1027下午考试总结

T1

​ 题目大意:给定一个$n$位数字,现在可以从中删除$s$个数字,问剩下的最小的数是多少.\(n <= 1e6\)

​ 按位贪心.

​ 越高位数字越小肯定越优.我们记录一下每个数字出现的位置都有哪些,然后从最高位往下走,填的数越小越好,并且要判断一下填完这个数后面是否有足够的数字给接下来的位置填.最后记得清除前导零就好了.复杂度$O(n * 10)$.

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
char ch[N];
int s, len;
int num[N], ans[N], now[10];
vector<int> pos[10];

int main() {
	
	cin >> (ch + 1) >> s; len = strlen(ch + 1);
	for(int i = 1;i <= len; i++) num[i] = ch[i] - '0', pos[num[i]].push_back(i);
	int last = 0;
	for(int i = 1;i <= len - s; i++) {
		for(int j = 0;j <= 9; j++) {
			if(!(int)pos[j].size() || now[j] >= (int)pos[j].size()) continue;
			int now_pos = pos[j][now[j]];
			if(len - now_pos >= len - s - i && now_pos > last) { 
				ans[i] = j; now[j] ++; last = now_pos; break;
			}
		} 
		for(int j = 1;j <= 9; j++)
			if((int)pos[j].size()) while(now[j] < (int)pos[j].size() && pos[j][now[j]] <= last) now[j] ++;
	}
	int tag = 0;
	for(int i = 1;i <= len - s; i++) 
		if(!ans[i] && !tag) continue;
		else tag = 1, printf("%d", ans[i]);
	if(tag == 0) printf("0");
		
	return 0;
}

T2

​ 题目大意:

​ 有$n$个人要接种疫苗,每个人可以直接去政府那里接种,代价为$a_i$,也可以从已经接种过的人那里接种,代价为$k_$,$j$是已经接种过的人,问怎样接种可以使总代价最小.\(n <= 300\).

​ 最小生成树.

​ 我们把所有人之间连边,代价就是$k_$,再将每个人与政府(超级源点)连边,代价为$a_i$,然后跑最小生成树就好了.

#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 = 305;
int n, cnt, ans;
int a[N], fa[N];
struct edge { int to, val, from; } e[N * N * 2];

void add(int x, int y, int z) {
	e[++ cnt].to = y; e[cnt].val = z; e[cnt].from = x;
}

int cmp(edge a, edge b) { return a.val < b.val; }

int find(int x) {
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int main() {
	
	n = read();
	for(int i = 0;i <= n; i++) fa[i] = i;
	for(int i = 1;i <= n; i++) a[i] = read(), add(0, i, a[i]);
	for(int i = 1, x;i <= n; i++) 
		for(int j = 1;j <= n; j++) {
			x = read(); if(i != j) add(i, j, x);
		}
	sort(e + 1, e + cnt + 1, cmp); int num = 0;
	for(int i = 1;i <= cnt; i++) {
		int x = e[i].from, y = e[i].to;
		int tx = find(x), ty = find(y);
		if(tx != ty) {
			num ++; fa[tx] = ty; ans += e[i].val;
		}
		if(num == n) break;
	}
	printf("%d", ans);

	return 0;
}

T3

​ 题目大意:

​ 有 n 个建筑,建筑以及建筑间的道路形成一棵树。从建筑 a 到建筑 b 要花费[dis(a,b) xor M]秒。为保证安全,这些建筑的消息一直采用统一传输的方式,即在 n 个建筑中挑选一个改为仓库,存储并分发消息。为了给仓库选址,小 R 想知道,建筑i(1<=i<=n)到其它所有建筑花费的时间之和。\(n <= 1e5, m <= 15\)

​ 换根DP.

​ 我们发现$m$很小,所以可以将二进制的后几位(m会影响到的那几位)和前几位拆开来考虑.

​ $f[i]$表示以1为根节点,$i$到它的子树内的点的距离的前几位之和.$s[i][j]$表示以1为根节点,$i$到它的子树内的点的距离的后几位为$j$的个数是多少.

​ $g[i]$表示以$i$为根节点,所有的点到$i$的距离的前几位之和,$t[i][j]$表示以$x$为根节点,所有点到$i$距离的后几位为$j$的个数是多少.

​ 那么$ans[i]$就是:\(g[i] + \displaystyle \sum_{i = 1}^{res}s[i][j] * (j \ xor \ m)\).($res$就是二进制下m影响的位数, 就是那后几位)

​ $f[i]$和$s[i][j]$还是很好求的,看代码就好了,那么$g[i]$和$t[i][j]$怎么求呢?看代码吧画画图就好理解了

#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 n, m, cnt, res;
int s[N][20], t[N][20], head[N];
long long ans, f[N], g[N];
struct edge { int nxt, to, val; } e[N << 1];

void add(int x, int y, int z) {
	e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].val = z;
}

void get_res() {
	while(m) res ++, m >>= 1; res = (1 << res);
}

void get_f_s(int x, int fa) {
	s[x][0] ++;
	for(int i = head[x]; i ; i = e[i].nxt) {
		int y = e[i].to; if(y == fa) continue;
		get_f_s(y, x);
		for(int j = 0;j < res; j++) {
			f[x] += 1ll * s[y][j] * ((j + e[i].val) - (j + e[i].val) % res); //取出后几位的数
			s[x][(j + e[i].val) % res] += s[y][j]; // 从y到x经过了一条e[i].val的边,那么原来的j就会变成j + e[i].val
		} 
		f[x] += f[y];
	}
}

void get_g_t(int x, int fa) {
	if(x == 1) {
		g[x] = f[x];
		for(int j = 0;j < res; j++) t[x][j] = s[x][j];
	}
	for(int i = head[x]; i ; i = e[i].nxt) {
		int y = e[i].to; if(y == fa) continue;
		g[y] = g[x]; 
		int tmp[20];
		for(int j = 0;j < res; j++) {
			g[y] -= 1ll * s[y][j] * ((j + e[i].val) - (j + e[i].val) % res);  //把根从x换到y,那么y子树内的点就会减去一个e[i].val,其他的点就会加上一个e[i].val
			tmp[j] = t[x][j]; t[y][j] = s[y][j]; 
		}
		for(int j = 0;j < res; j++) tmp[(j + e[i].val) % res] -= s[y][j]; //这里减去是为了求出y子树外的点
		for(int j = 0;j < res; j++) {
			g[y] += 1ll * tmp[j] * ((j + e[i].val) - (j + e[i].val) % res);
			t[y][(j + e[i].val) % res] += tmp[j];
		}
		get_g_t(y, x);
	}
}

int main() {
	
	n = read(); m = read(); int M = m;
	for(int i = 1, x, y, z;i <= n - 1; i++) {
		x = read(); y = read(); z = read();
		add(x, y, z); add(y, x, z);
	}
	get_res(); get_f_s(1, 0); get_g_t(1, 0);
	for(int i = 1;i <= n; i++) {
		long long tmp = 0;
		t[i][0] --;
		for(int j = 0;j < res; j++) tmp += 1ll * t[i][j] * (j ^ M);
		printf("%lld\n", tmp + g[i]);
	}
	
	return 0;
}
posted @ 2020-10-28 21:58  C锥  阅读(77)  评论(0编辑  收藏  举报