ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

牛客NOIP暑期七天营-提高组1 解题报告

2019-08-19 23:56:16  阅读:248  来源: 互联网

标签:cnt putchar NOIP int 短路 pos 暑期 牛客 ll


https://ac.nowcoder.com/acm/contest/920#question

A

构造+双指针
发现m的限制是1e5,而点数是5e4,所以不能构造太多的边,思考一下最短路树的定义。会发现其实就是要构造出一个最短路树。按\(a_i\)升序排序,那么只需要找一个在\(a_i-S\)的点连边即可。这个玩意可以直接用双指针或者二分或者其他什么数据结构来实现。判断无解即判断是否存在大于S的边或者0边。复杂度\(O(n \log n)\)

#include <bits/stdc++.h>
using namespace std;

namespace io {
char buf[1<<21], *p1 = buf, *p2 = buf;
inline char gc() {
    if(p1 != p2) return *p1++;
    p1 = buf;
    p2 = p1 + fread(buf, 1, 1 << 21, stdin);
    return p1 == p2 ? EOF : *p1++;
}
#define G gc

#ifndef ONLINE_JUDGE
#undef G
#define G getchar
#endif

template<class I>
inline void read(I &x) {
    x = 0; I f = 1; char c = G();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = G(); }
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = G(); }
    x *= f;
}
template<class I>
inline void write(I x) {
    if(x == 0) {putchar('0'); return;}
    I tmp = x > 0 ? x : -x;
    if(x < 0) putchar('-');
    int cnt = 0;
    while(tmp > 0) {
        buf[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while(cnt > 0) putchar(buf[--cnt]);
}

#define in(x) read(x)
#define outn(x) write(x), putchar('\n')
#define out(x) write(x), putchar(' ')

} using namespace io;

#define ll long long
const int N = 100010;

int n, S, m;
struct Node {int v, id;} a[N]; 
struct edge {int u, v, w;} e[N];

bool operator < (Node a, Node b) {
    return a.v < b.v;
}

int main() {
    read(n); read(S);
    for(int i = 1; i <= n; ++i) read(a[i].v), a[i].id = i;
    sort(a + 1, a + n + 1);
    bool flag = 0; int last = 1;
    for(int i = 2; i <= n; ++i) {
        if(a[i].v == 0) {flag = 1;  break;}
        while(last < i && a[i].v - a[last].v > S) ++last;
        e[++m] = {a[last].id, a[i].id, a[i].v - a[last].v};
    }
    for(int i = 1; i <= m; ++i) if(e[i].w > S || e[i].w < 1) flag = 1;
    if(flag) puts("-1");
    else {
        printf("%d\n", m);
        for(int i = 1; i <= m; ++i) printf("%d %d %d\n", e[i].u, e[i].v, e[i].w);
    }
    return 0;
} 

B

构造+贪心+01trie
因为是一个完全图,所以题目等价于,1到n的排列中,相邻两个数作为下标,\(a_i\)的\(xor\)最大值最小是多少。
因为高位对答案的影响更大,按位考虑,我们肯定需要让当前位为1的排在一块,为0的排在一块,那么答案的最大值一定在中间接壤点(因为这两部分内部再怎么xor也没这一位的一个1贡献大)。
所以找出有0也有1的最高位,然后将序列划分为两段。那么问题就变成了,在序列0和序列1中各选出一个数,使他们的xor和尽可能小。这个01Trie维护一下就好了。
赛时是写了一个同样思想的归并排序,但是没有想到“这两部分内部再怎么xor也没这一位的一个1贡献大”这一性质,所以写挂了...

#include <bits/stdc++.h>
using namespace std;

namespace io {
char buf[1<<21], *p1 = buf, *p2 = buf;
inline char gc() {
    if(p1 != p2) return *p1++;
    p1 = buf;
    p2 = p1 + fread(buf, 1, 1 << 21, stdin);
    return p1 == p2 ? EOF : *p1++;
}
#define G gc

#ifndef ONLINE_JUDGE
#undef G
#define G getchar
#endif

template<class I>
inline void read(I &x) {
    x = 0; I f = 1; char c = G();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = G(); }
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = G(); }
    x *= f;
}
template<class I>
inline void write(I x) {
    if(x == 0) {putchar('0'); return;}
    I tmp = x > 0 ? x : -x;
    if(x < 0) putchar('-');
    int cnt = 0;
    while(tmp > 0) {
        buf[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while(cnt > 0) putchar(buf[--cnt]);
}

#define in(x) read(x)
#define outn(x) write(x), putchar('\n')
#define out(x) write(x), putchar(' ')

} using namespace io;

#define ll long long
const int N = 1000100;

int n, t[N * 10][2], tot;
ll a[N], b[N][2];

void insert(ll x) {
    int u = 0;
    for(ll pos = 60; pos >= 0; --pos) {
        int c = (x >> pos) & 1LL;
        if(!t[u][c]) t[u][c] = ++tot;
        u = t[u][c];
    }
}

ll query(ll x) {
    int u = 0; ll ans = 0;
    for(ll pos = 60; pos >= 0; --pos) {
        int c = (x >> pos) & 1LL;
        if(t[u][c]) u = t[u][c];
        else u = t[u][c ^ 1], ans += 1LL << pos; 
    }
    return ans;
}

int main() {
    read(n);
    for(int i = 1; i <= n; ++i) read(a[i]);
    int cnt[2];
    for(ll pos = 60; pos >= 0; --pos) {
        cnt[0] = cnt[1] = 0;
        for(int i = 1; i <= n; ++i) {
            int c = (a[i] >> pos) & 1; 
            b[++cnt[c]][c] = a[i];
        }
        if(cnt[0] && cnt[1]) break;
    }
    if(!cnt[0] || !cnt[1]) return puts("0"), 0;
    for(int i = 1; i <= cnt[0]; ++i) insert(b[i][0]);
    ll ans = 1LL << 61;
    for(int i = 1; i <= cnt[1]; ++i) ans = min(ans, query(b[i][1]));
    outn(ans);
}

C

最短路树+spfa
同起点到两个点的最短路的公共部分等价于该起点的最短路树上这两个点的lca到根的距离。字典序最小的话建最短路树的时候处理一下就好。将询问离线下来,每次处理出当前起点下的最短路树,然后对每个询问依次取max。这样就只用开一个最短路树的空间了。复杂度是O(nqlogn + nmk)(k为spfa中的那个常数)。正解是在建最短路树那里用Johnson算法,卡了spfa。因为过于码农所以没补这题= =记录了一下思想。
然后johnson算法的话,就是定义一个虚点,然后到每个点连长度为0的边,然后spfa一次跑出最短路,然后这个最短路就是每个节点的势能函数,记为h。那么将边权改为h[v]-h[u]+w,就能保证不存在负边权了,于是跑DIjkstra,最后将势能函数反推回去就可以得到最短路了。
这个玩意好像在rqy的Dijkstra跑费用流的课件里面看到过。

标签:cnt,putchar,NOIP,int,短路,pos,暑期,牛客,ll
来源: https://www.cnblogs.com/henry-1202/p/11380542.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有