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\),分两种情况:
- \(a[i]\leq a[i-1]\),不需要额外多,只需要前面的顺便把区间扩大即可。即继承前面答案。
- \(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;
}