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;
}

猜你喜欢

转载自www.cnblogs.com/Garen-Wang/p/11619073.html
今日推荐