NOIP2013 复盘

NOIP2013复盘

D1T1 P1965 转圈游戏

要你求\(x+m \times 10^k\)在膜\(n\)下的值。

ksm模板。

D1T2 P1966 火柴排队

\(\sum(a_i-b_i)^2=a_i^2+b_i^2-2a_ib_i\),平方项无论怎么排都会有,要增大的是\(a_ib_i\)这项。

由排序不等式知:两个数越接近,数值越大。

所以应该拿\(a\)数组和\(b\)数组分别排序,然后分别对应,这项会是最大的。

现在的问题是如何把\(a\)的数字大小关系变成\(b\)

因为具体数字大小没影响,于是我们统统离散化。

\(c[a[i]]=b[i]\),要让答案最优必须要是\(c[a[i]]=a[i]\),也就是\(c[i]=i\)

所以就变成了把\(c\)数组变成从\(1\)\(n\)数组的最少操作。

这不就是逆序对吗?树状数组或归并排序写起。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100005, mod = 99999997;
int a[maxn], b[maxn], c[maxn], d[maxn], q[maxn], t[maxn], ans;
int n;
bool cmp1(int x, int y)
{
	return a[x] < a[y];
}
bool cmp2(int x, int y)
{
	return b[x] < b[y];
}
int read()
{
	int ans = 0, s = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0')
	{
		if(ch == '-') s = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9')
	{
		ans = ans * 10 + ch - '0';
		ch = getchar();
	}
	return s * ans;
}
void msort(int l, int r)
{
	int mid = (l + r) >> 1;
	if(l != r) msort(l, mid), msort(mid + 1, r);
	int i = l, j = mid + 1, k = l;
	while(i <= mid && j <= r)
	{
		if(q[i] > q[j])
		{
			ans = (ans + j - k) % mod;
			t[k++] = q[j++];
		}
		else t[k++] = q[i++];
	}
	while(i <= mid) t[k++] = q[i++];
	while(j <= r) t[k++] = q[j++];
	for(int i = l; i <= r; i++) q[i] = t[i];
}
int main()
{
	n = read();
	for(int i = 1; i <= n; i++)
	{
		a[i] = read();
		c[i] = i;
	}
	for(int i = 1; i <= n; i++)
	{
		b[i] = read();
		d[i] = i;
	}
	sort(c + 1, c + 1 + n, cmp1);
	sort(d + 1, d + 1 + n, cmp2);
	for(int i = 1; i <= n; i++) q[c[i]] = d[i];
	msort(1, n);
	printf("%d\n", ans);
	return 0;
}

D1T3 P1967 货车运输

不难证明(猜到)每条走过的边都会是这个图上最大生成树的边,不然会亏。

所以求最大生成树,利用kruskal的做法,改一下符号即可。

接下来就变成最基础的树上路径最小值,利用树剖和线段树可以维护。

原来这道题还可以暴力跳lca的啊。。。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 10005, maxm = 50005, INF = 19260817;
int n, m, q;
//kruskal
struct edge
{
	int u, v, w;
} ee[maxm];
int f[maxn];
//new graph
struct Edge
{
	int next, to, weight;
} e[maxm << 1];
int head[maxn], etot;
//shupou
int dep[maxn], size[maxn], wson[maxn], fa[maxn];
int top[maxn], dfn[maxn], pre[maxn], dtot;
int path[maxn];
//functions
int read()
{
	int ans = 0, s = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0')
	{
		if(ch == '-') s = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9')
	{
		ans = ans * 10 + ch - '0';
		ch = getchar();
	}
	return s * ans;
}
int find(int x)
{
	if(f[x] == x) return x;
	return f[x] = find(f[x]);
}
bool cmp(const edge x, const edge y)
{
	return x.w > y.w;
}
void link(int u, int v, int w)
{
	e[++etot] = (Edge){head[u], v, w};
	head[u] = etot;
}
void kruskal()
{
	for(int i = 1; i <= n; i++) f[i] = i;
	sort(ee + 1, ee + m + 1, cmp);
	for(int i = 1; i <= m; i++)
	{
		int x = find(ee[i].u), y = find(ee[i].v), w = ee[i].w;
		if(x != y)
		{
			link(x, y, w); link(y, x, w);
			f[x] = y;
		}
	}
}
void dfs1(int u, int ff, int w)
{
	size[u] = 1; fa[u] = ff; dep[u] = dep[ff] + 1; path[u] = w;
	for(int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if(v == ff) continue;
		dfs1(v, u, e[i].weight);
		size[u] += size[v];
		if(size[v] > size[wson[u]]) wson[u] = v;
	}
}
void dfs2(int u, int topf)
{
	dfn[u] = ++dtot; pre[dtot] = u; top[u] = topf;
	if(wson[u]) dfs2(wson[u], topf);
	for(int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if(v == fa[u] || v == wson[u]) continue;
		dfs2(v, v);
	}
}
int getlca(int u, int v)
{
	if(find(u) != find(v)) return -1;
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	return u; 
}
int getans(int u, int v, int lca)
{
	if(lca == -1) return -1;
	int ans = INF;
	while(u != lca)
	{
		ans = min(ans, path[u]);
		u = fa[u];
	}
	while(v != lca)
	{
		ans = min(ans, path[v]);
		v = fa[v];
	}
	return ans;
}
int main()
{
	n = read(), m = read();
	for(int i = 1; i <= m; i++)
	{
		ee[i] = (edge){read(), read(), read()};
	}
	kruskal();
	for(int i = 1; i <= n; i++)
	{
		if(dep[i] == 0)
		{
			dfs1(i, 0, 0); dfs2(i, i);
		}
	}
	q = read();
	while(q--)
	{
		int x = read(), y = read();
		printf("%d\n", getans(x, y, getlca(x, y)));
	}
	return 0;
}

D2T1 P1969 积木大赛

NOIP2018原题纪念,可惜我没有做到

分治的做法可以被卡到\(O(n^2)\),正解其实是\(O(n)\)的递推。

\(ans[i]\)为前\(i\)个的答案,则对于遍历得的每个\(i\),分两种情况:

  1. \(a[i]\leq a[i-1]\),不需要额外多,只需要前面的顺便把区间扩大即可。即继承前面答案。
  2. \(a[i]>=a[i-1]\),这时就需要额外的\(a[i]-a[i-1]\)次操作把这个东西补齐。

这种做法是成立的,因为补齐的操作在2情况已经计算完毕。

D2T2 P1970 花匠

这道题有两种解法,一种是玄学贪心,一种是dp。

dp做法是设一个\(up[i]\)\(down[i]\)记录前\(i\)个当前正在上升或下降的序列最大长度。

如果碰到递增,就从正下降的取出长度最大值再加1,递减的同理。

同时,如果不需要更新的时候,继承前面的最大的长度即可。

代码清奇:

#include<cstdio>
using namespace std;
const int maxn = 100005;
inline int max(int a, int b)
{
	if(a > b) return a;
	return b;
}
int a[maxn], up[maxn], down[maxn];
int n;
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	up[1] = down[1] = 1;
	for(int i = 2; i <= n; i++)
	{
		if(a[i - 1] < a[i]) up[i] = down[i - 1] + 1;
		else up[i] = up[i - 1];
		if(a[i - 1] > a[i]) down[i] = up[i - 1] + 1;
		else down[i] = down[i - 1];
	}
	printf("%d\n", max(up[n], down[n]));
	return 0;
}

玄学贪心做法是这样的:

#include<bits/stdc++.h>
using namespace std;
int n,h[1000005],ans=1;bool con;
int main()
{
    cin>>n;for(int i=1;i<=n;i++) cin>>h[i];
    if(h[2]>=h[1]) con=1;
    for(int i=1;i<=n;i++)
    {
        if(con==0&&i==n) {ans++;break;}
        if(con==1) if(h[i+1]<h[i]){ans++;con=0;continue;}
        if(con==0) if(h[i+1]>h[i]) {ans++;con=1;continue;}
    }
    cout<<ans; 
}

通过第一个和第二个判断决策方向,然后那些单调递增或递减的只取最后一个,显然答案是最优的。

那一步ans++其实不是很懂。

这个代码可以被多个重复的数据卡,但是这道题的数据是均匀分布的随机数。

D2T3 P1979 华容道

https://www.luogu.org/blog/moisture2333/solution-p1979w

#include<bits/stdc++.h>
const int maxn = 31;
const int INF = 0x3f3f3f3f;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
int n, m, Q;
int ex, ey, bx, by, cx, cy;
int G[maxn][maxn];
bool vis[maxn][maxn];
bool ok[maxn][maxn][4];

struct Edges {
	int next, to, weight;
} e[200005];
int head[10005], tot;
bool visit[10005];
int dist[10005];
void link(int u, int v, int w) {
	e[++tot] = (Edges){head[u], v, w};
	head[u] = tot;
}
int getid(int x, int y, int dir) {
	return 4 * ((x - 1) * m + y) - 4 + dir;
}
int query(int x, int y) {
	if(x < 1 || x > n || y < 1 || y > m) return 0;
	return G[x][y];
}
int bfs(int nx, int ny, int sx, int sy, int tx, int ty) {
	// ¿Õ°×¸ñ×ÓÂÒת ¶øÄ¿±êÆå×Ó²»Òƶ¯ 
	struct Nodes {
		int x, y, dis;
	};
	std::queue<Nodes> q;
	memset(vis, false, sizeof vis);
	q.push((Nodes){sx, sy, 0}); vis[sx][sy] = true;
	while(!q.empty()) {
		Nodes sb = q.front(); q.pop();
		if(sb.x == tx && sb.y == ty) return sb.dis;
		for(int i = 0; i < 4; i++) {
			int newx = sb.x + dx[i], newy = sb.y + dy[i];
			if(query(newx, newy)) {
				if(vis[newx][newy]) continue;
				if(newx == nx && newy == ny) continue;
				q.push((Nodes){newx, newy, sb.dis + 1}); vis[newx][newy] = true;
			}
		}
	}
	return INF;
}
void init() {
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			if(!G[i][j]) continue;
			for(int k = 0; k < 4; k++) {
				if(query(i + dx[k], j + dy[k])) ok[i][j][k] = true;
			}
		}
	}
	
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			for(int k = 0; k < 4; k++) {
				for(int l = k + 1; l < 4; l++) {
					if(ok[i][j][k] && ok[i][j][l]) {
						int a = getid(i, j, k), b = getid(i, j, l);
						int temp = bfs(i, j, i + dx[k], j + dy[k], i + dx[l], j + dy[l]);
						if(temp == INF) continue;
						link(a, b, temp); link(b, a, temp);
					}
				}
			}
		}
	}
	
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j < m; j++) {
			if(ok[i][j][3] && ok[i][j + 1][2]) {
				int a = getid(i, j, 3), b = getid(i, j + 1, 2);
				link(a, b, 1); link(b, a, 1);
			}
		}
	}
	for(int i = 1; i < n; i++) {
		for(int j = 1; j <= m; j++) {
			if(ok[i][j][1] && ok[i + 1][j][0]) {
				int a = getid(i, j, 1), b = getid(i + 1, j, 0);
				link(a, b, 1); link(b, a, 1);
			}
		}
	}
}
void solve() {
	std::queue<int> q;
	memset(dist, 0x3f, sizeof dist);
	for(int i = 0; i < 4; i++) {
		int newx = bx + dx[i], newy = by + dy[i];
		if(query(newx, newy)) {
			int temp = bfs(bx, by, ex, ey, newx, newy);
			if(temp == INF) continue;
			int idx = getid(bx, by, i);
			dist[idx] = temp;
			q.push(idx); visit[idx] = true;
		}
	}
	while(!q.empty()) {
		int u = q.front(); q.pop(); visit[u] = false;
		for(int i = head[u]; i; i = e[i].next) {
			int v = e[i].to;
			if(dist[v] > dist[u] + e[i].weight) {
				dist[v] = dist[u] + e[i].weight;
				if(!visit[v]) {
					q.push(v); visit[v] = true;
				}
			}
		}
	}
	int ans = INF;
	for(int i = 0; i < 4; i++) {
		int temp = getid(cx, cy, i);
		ans = std::min(ans, dist[temp]);
	}
	if(ans == INF) printf("-1\n");
	else printf("%d\n", ans);
}
int main() {
	scanf("%d %d %d", &n, &m, &Q);
	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &G[i][j]);
	init();
	while(Q--) {
		scanf("%d %d %d %d %d %d", &ex, &ey, &bx, &by, &cx, &cy);
		if(bx == cx && by == cy) {
			printf("0\n");
		} else if(!G[cx][cy] || !G[bx][by]) {
			printf("-1\n");
		} else solve();
	}
	return 0;
}
posted @ 2019-10-03 09:10  Garen-Wang  阅读(194)  评论(0编辑  收藏  举报