NOI 2015题解

[luogu P2168] [NOI2015]荷马史诗

就是k叉哈夫曼树
考虑二叉的时候就是每次取最小的两个合并
k叉就是每次取k个合并
这题唯一要注意的就是要补0
code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n, k;
ll x;
priority_queue<pair<ll, ll>, vector<pair<ll, ll> >, greater<pair<ll, ll> > > q;
int main() {
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i ++) scanf("%lld", &x), q.push(make_pair(x, 0));
	for(int i = 1; i <= (k - 1 - (n - 1) % (k - 1)) % (k - 1); i ++) q.push(make_pair(0, 0));
	ll ans = 0;
	while(q.size() > 1) {
		ll s = 0, dep = 0;
		for(int i = 1; i <= k; i ++) {
			s += q.top().first;
			dep = max(dep, q.top().second); q.pop();
		}
		ans += s, q.push(make_pair(s, dep + 1));
	}
	printf("%lld\n%lld", ans, q.top().second);
	return 0;
}

[luogu P1955] [NOI2015]程序自动分析

首先离散化
然后先考虑相等的,可以用并查集维护联通快
然后不等的就相当于询问x, y是否在同一个联通块里面
code:


#include<bits/stdc++.h>
#define N 2000005
using namespace std;
struct A {
	int x, y, z;
} a[N];
int cmp(A x, A y) {
	return x.z > y.z;
}
int fa[N], t, n, sz, b[N];
int get(int x) {
	return fa[x] == x? x : (fa[x] = get(fa[x]));	
}
void merge(int x, int y) {
	x = get(x), y = get(y);
	fa[x] = y;
}
int main() {
	scanf("%d", &t);
	while(t --) {
		scanf("%d", &n);
		for(int i = 1; i <= n; i ++) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z), b[++ sz] = a[i].x, b[++ sz] = a[i].y;
		sort(b + 1, b + 1 + sz);
		for(int i = 1; i <= n; i ++) {
			a[i].x = lower_bound(b + 1, b + 1 + sz, a[i].x) - b;
			a[i].y = lower_bound(b + 1, b + 1 + sz, a[i].y) - b;
		}
		for(int i = 1; i <= sz; i ++) fa[i] = i;
		sort(a + 1, a + 1 + n, cmp);
		for(int i = 1; i <= n; i ++) if(a[i].z == 1) merge(a[i].x, a[i].y);
		int F = 0;
		for(int i = 1; i <= n; i ++) if(a[i].z == 0) {
			int x = get(a[i].x), y = get(a[i].y);
			if(x == y) F = 1;
		}		
		if(F) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}

[P2146] [NOI2015]软件包管理器

树剖板子题,没什么好说的
code:

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
struct edge {
	int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
	memset(p, -1, sizeof p);
	eid = 0;
}
void insert(int u, int v) {
	e[eid].v = v;
	e[eid].nxt = p[u];
	p[u] = eid ++;
}
int fa[N], dep[N], size[N], w[N], top[N], id[N], tot, Lef[N], Righ[N], n, q;
void dfs(int u) {
	size[u] = 1;
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int v = e[i].v;
		dep[v] = dep[u] + 1, fa[v] = u;
		dfs(v);
		if(size[v] > size[w[u]]) w[u] = v;
		size[u] += size[v];
	}
}
void dfss(int u) { 
	id[u] = ++ tot; Lef[u] = tot;
	if(w[u]) top[w[u]] = top[u], dfss(w[u]);
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int v = e[i].v;
		if(v == w[u]) continue;
		top[v] = v;
		dfss(v);
	}
	Righ[u] = tot;
}

int sum[N << 3], tag[N << 3];
void update(int rt) {
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt, int l, int r) {
	int mid = (l + r) >> 1;
	if(tag[rt] == 1) {
		sum[rt << 1] = mid - l + 1;
		sum[rt << 1 | 1] = r - mid;
		tag[rt << 1] = tag[rt << 1 | 1] = tag[rt];
	} 
	if(tag[rt] == 2) {
		sum[rt << 1] = sum[rt << 1 | 1] = 0;
		tag[rt << 1] = tag[rt << 1 | 1] = tag[rt];
	}
	tag[rt] = 0;
}
void add(int rt, int l, int r, int L, int R) {
	if(L <= l && r <= R) {
		sum[rt] = r - l + 1;
		tag[rt] = 1;
		return;	
	} 
	pushdown(rt, l, r);
	int mid = (l + r) >> 1;
	if(L <= mid) add(rt << 1, l, mid, L, R);
	if(R > mid) add(rt << 1 | 1, mid + 1, r, L, R);
	update(rt);
}
void del(int rt, int l, int r, int L, int R) {
	if(L <= l && r <= R) {
		sum[rt] = 0;
		tag[rt] = 2;
		return;	
	} 
	pushdown(rt, l, r);
	int mid = (l + r) >> 1;
	if(L <= mid) del(rt << 1, l, mid, L, R);
	if(R > mid) del(rt << 1 | 1, mid + 1, r, L, R);
	update(rt);
}
int query(int rt, int l, int r, int L, int R) { //printf("%d  %d %d %d %d  %d\n", rt, l, r, L, R, sum[rt]);
	if(L <= l && r <= R) return sum[rt];
	pushdown(rt, l, r);
	int mid = (l + r) >> 1, ret = 0;
	if(L <= mid) ret += query(rt << 1, l, mid, L, R);
	if(R > mid) ret += query(rt << 1 | 1, mid + 1, r, L, R);
	return ret;
}
void Add(int x) {
	while(top[x] != 1) {
		add(1, 1, n, id[top[x]], id[x]);
		x = fa[top[x]];
	}
	add(1, 1, n, id[top[x]], id[x]);
}
void Del(int x) {
	while(top[x] != 1) {
		del(1, 1, n, id[top[x]], id[x]);
		x = fa[top[x]];
	}
	del(1, 1, n, id[top[x]], id[x]);
}
int Query(int x) {
	int ret = 0;
	while(top[x] != 1) { //printf("%d ", x);
		ret += query(1, 1, n, id[top[x]], id[x]);
		x = fa[top[x]];
	}
	ret += query(1, 1, n, id[top[x]], id[x]);
	return ret;
}
int main() { init();
	scanf("%d", &n);
	for(int i = 2, x; i <= n; i ++) {
		scanf("%d", &x);
		x ++;
		insert(x, i);
	}
	fa[1] = 1, dep[1] = 1, top[1] = 1;
	dfs(1);
	dfss(1);
	scanf("%d", &q);
	while(q --) {
		char ch[13]; int x;
		scanf(" %s %d", &ch, &x);
		x ++;
		if(ch[0] == 'i') {
			printf("%d\n", dep[x] - Query(x));
			Add(x);
		} else {
			printf("%d\n", query(1, 1, n, Lef[x], Righ[x]));
			del(1, 1, n, Lef[x], Righ[x]);
		}
	}
	return 0;
}

[P2178] [NOI2015]品酒大会

to be continue……

P2150 [NOI2015]寿司晚宴

考虑 n < = 30 n<=30 n<=30质数只有10个
然后直接状压 d p [ S 1 ] [ S 2 ] dp[S1][S2] dp[S1][S2]表示甲选的集合为S1,乙选的集合为S2
DP方程显然
然后考虑 n < = 500 n<=500 n<=500
发现除了最大的质数,其他的都是 < = s q r t ( n ) <=sqrt(n) <=sqrt(n)
只有 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 2,3,5,7,11,13,17,19 2,3,5,7,11,13,17,19这8个
然后按照最大的质数因子排序,相同的不在一起转移就行了
注意一下不选的情况是算重复的,要减掉
code:

#include<bits/stdc++.h>
#define int long long
#define N 505
using namespace std;
int prime[12] = {2, 3, 5, 7, 11, 13, 17, 19};
int n, mod, dp[N][N], f1[N][N], f2[N][N];
struct A {
   int sta, big;
} a[N];
int cmp(A x, A y) {
   return x.big < y.big;
}
void MOD(int & x) {
   x = (x + mod) % mod;
}
signed main() {
   scanf("%lld%lld", &n, &mod);
   for(int i = 2; i <= n; i ++) {
   	int x = i;
   	for(int j = 0; j < 8; j ++) {
   		while(x % prime[j] == 0) x /= prime[j], a[i].sta |= (1 << j);
   	}
   	a[i].big = x;
   }
   sort(a + 1, a + 1 + n, cmp);
   dp[0][0] = 1;
   for(int k = 2; k <= n; k ++) {
   	if(a[k].big == 1 || a[k].big != a[k - 1].big) {
   		for(int i = 0; i <= 255; i ++)
   			for(int j = 0; j <= 255; j ++)
   				f1[i][j] = f2[i][j] = dp[i][j];
   	}
   	for(int i = 255; i >= 0; i --)
   		for(int j = 255; j >= 0; j --) {
   			if(i & j) continue;
   			if((j & a[k].sta) == 0) f1[i | a[k].sta][j] += f1[i][j], MOD(f1[i][j]);
   			if((i & a[k].sta) == 0) f2[i][j | a[k].sta] += f2[i][j], MOD(f2[i][j]); 
   		}
   	if(a[k].big == 1 || a[k].big != a[k + 1].big) {
   		for(int i = 0; i <= 255; i ++)
   			for(int j = 0; j <= 255; j ++)
   				dp[i][j] = f1[i][j] + f2[i][j] - dp[i][j], MOD(dp[i][j]);//减去算重复的
		}
   }
   int ans = 0;
   for(int i = 0; i <= 255; i ++)
   	for(int j = 0; j <= 255; j ++)
   		ans += dp[i][j], MOD(ans);
   printf("%lld", ans);
   return 0;
}
posted @ 2020-11-22 21:42  lahlah  阅读(31)  评论(0编辑  收藏  举报