NOIP2012 复盘

NOIP2012复盘

D1T1 P1079 Vigenère 密码

只要把A到Z换成0到25,那么这个运算就变成了一个膜为26的加法了。

记得不够的时候将\(k\)重复使用即可。

代码:

#include<iostream>
using namespace std;
string key, str;
char cal(char k, char s)
{
    if(k >= 'A' && k <= 'Z') k += 32;
    if(s >= 'A' && s <= 'Z') s += 32;
    char ans = s - k + 'a';
    while(ans < 'a') ans += 26;
    while(ans > 'z') ans -= 26;
    return ans;
}
int main()
{
    cin >> key >> str;
    for(int i = 0; i < str.length(); i++)
    {
        bool big = str[i] >= 'A' && str[i] <= 'Z';
        char temp = cal(key[i % key.length()], str[i]);
        if(big) temp -= 32;
        cout << temp;
    }
    cout << endl;
    return 0;
}

D1T2 P1080 国王游戏

显然不可以遍历所有的排列获得最优解,不过我们可以拿来对拍。

我们考虑一个国王两个大臣的情况。国王左右手为\(a\)\(b\),大臣A左右手为\(a_1\)\(b_1\),大臣B为\(a_2\)\(b_2\)

如果是国王 A B这么排的话,A是\(\frac{a}{b_1}\),B是\(\frac{a\times a_1}{b_2}\)\(ans_1\)是两个的最大值。

如果是国王 B A这么排的话,A是\(\frac{a}{b_2}\),B是\(\frac{a\times a_2}{b_1}\)\(ans_2\)是两个的最大值。

可以知道\(\frac{a}{b_1}\leq \frac{a\times a_2}{b_1}\)\(\frac{a}{b_2} \leq \frac{a \times a_1}{b_2}\)

\(ans_1<ans_2\),则\(\frac{a \times a_1}{b_2}<\frac{a\times a_2}{b_1}\),即\(a_1b_1<a_2b_2\)

发现推回去也是一样的,这是充要条件。

所以当我们把这两个大臣按\(a_ib_i\)从小到大排序时,会得到更小的答案。

按照这个原理,我们可以随机取任何的两个大臣,按交换位置前后去证明,结果是一样的。

所以结论是:\(a_ib_i\)从小到大排序时,能得到最优解。

套一个高精即可

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 1005;
struct Nodes
{
    int l, r;
    bool operator < (const Nodes &rhs) const
    {
        return l * r < rhs.l * rhs.r;
    }
} s[maxn];
int n;
int L, R;
struct INT
{
    int a[10005], len;
    INT()
    {
        memset(a, 0, sizeof a);
        len = 0;
    }
    void init(int x)
    {
        if(x == 0) len = 1;
        else
        {
            while(x)
            {
                a[len++] = x % 10;
                x /= 10;
            }
        }
    }
    bool operator < (const INT &rhs) const
    {
        if(len != rhs.len) return len < rhs.len;
        for(int i = len - 1; i >= 0; i--)
        {
            if(a[i] < rhs.a[i]) return true;
            else if(a[i] > rhs.a[i]) return false;
        }
        return false;
    }
    INT operator * (const int &rhs) const
    {
        INT ret;
        for(int i = 0; i < len; i++) ret.a[i] = a[i] * rhs;
        int llen;
        for(int i = 0; i < len || ret.a[i]; i++)
        {
            if(ret.a[i] / 10)
            {
                ret.a[i + 1] += ret.a[i] / 10;
                ret.a[i] %= 10;
            }
            llen = i;
        }
        if(ret.a[llen] == 0) ret.len = llen;
        else ret.len = llen + 1;
        return ret;
    }
    INT operator / (const int &x) const
    {
        INT ret;
        ret.len = len;
        int rest = 0;
        for(int i = len - 1; i >= 0; i--)
        {
            rest = rest * 10 + a[i];
            ret.a[i] = rest / x;
            rest %= x;
        }
        while(ret.len > 1 && ret.a[ret.len - 1] == 0) ret.len--;
        return ret;
    }
    void print()
    {
        for(int i = len - 1; i >= 0; i--) printf("%d", a[i]);
        printf("\n");
    }
};
int main()
{
    /*
    while(233)
    {
        int x, y; scanf("%d%d", &x, &y);
        INT xx; xx.init(x);
        INT yy; yy.init(y);
        INT res1 = xx * y, res2 = xx / y;
        res1.print();
        res2.print();
        printf("%d\n", xx < yy);
    }
    return 0;
    */
    
    scanf("%d%d%d", &n, &L, &R);
    for(int i = 1; i <= n; i++) scanf("%d%d", &s[i].l, &s[i].r);
    std::sort(s + 1, s + n + 1);
    INT temp; temp.init(L);
    INT ans;
    for(int i = 1; i <= n; i++)
    {
        ans = std::max(ans, temp / s[i].r);
        temp = temp * s[i].l;
    }
    ans.print();
    return 0;
    
}

D1T3 P1081 开车旅行

这道题暴力挺好写的,但是正解的倍增做法很难。

首先应该预处理出小A和小B在每个城市的下一个城市是哪个,也就是去找后面的最小点和次小点。

预处理有两种方法,第一种是建一颗以海拔为关键字的平衡树,依次插入找前驱后继即可。

第二种是离散化之后拿坐标去建双向链表,用很多次判断去找到最小点和次小点。

预处理之后设\(f[i][j]\)为从第\(i\)座城市开始,A和B每人都开了\(2^j\)天所到的点。

\(dpa[i][j]\)\(dpb[i][j]\)为上述路程中A B走的路程。

上面三个数组的递推都很简单,但是初始化不简单,需要注意。

第一问就可以遍历所有的起点城市,总共用\(O(n \log n)\)的复杂度回答。

第二问直接可以通过倍增从大到小慢慢凑,就可以求出路程总数。

代码细节很多,很不好写:

#include<bits/stdc++.h>
const int maxn = 100005;
int n, m, l, r, j;
struct Nodes {
    int val, idx, left, right;
    bool operator < (const Nodes &rhs) const {
        return val < rhs.val;
    }
} d[maxn];
int p[maxn];
int dpa[maxn][21], dpb[maxn][21], f[maxn][21];
int na[maxn], nb[maxn], a, b, ans = n;
double minv = 2147483647;
bool zuo() {
    if(!l) return false;
    if(!r) return true;
    return d[j].val - d[l].val <= d[r].val - d[j].val;
}
int pd(int a, int b) {
    if(!a) return d[b].idx;
    if(!b) return d[a].idx;
    if(d[j].val - d[a].val <= d[b].val - d[j].val) return d[a].idx;
    else return d[b].idx;
}
void init() {
    int i, j;
    for(j = 1; j <= 20; j++) {
        for(int i = 1; i <= n; i++) {
            f[i][j] = f[f[i][j - 1]][j - 1];
            dpa[i][j] = dpa[i][j - 1] + dpa[f[i][j - 1]][j - 1];
            dpb[i][j] = dpb[i][j - 1] + dpb[f[i][j - 1]][j - 1];
        }
    }
}
void solve(int s, long long x) {
    int i, j;
    a = b = 0;
    for(i = 20; i >= 0; i--) {
        if(f[s][i] && 0ll + a + b + dpa[s][i] + dpb[s][i] <= x) {
            a += dpa[s][i]; b += dpb[s][i];
            s = f[s][i];
        }
    }
    if(na[s] && a + b + dpa[s][0] <= x) a += dpa[s][0];
}
int main() {
    int i; long long x;
    scanf("%d", &n);
    for(i = 1; i <= n; i++) scanf("%d", &d[i].val), d[i].idx = i;
    std::sort(d + 1, d + n + 1);
    for(i = 1; i <= n; i++) {
        p[d[i].idx] = i; d[i].left = i - 1; d[i].right = i + 1;
    }
    d[1].left = d[n].right = 0;
    for(i = 1; i <= n; i++) {
        j = p[i]; l = d[j].left; r = d[j].right;
        if(zuo()) nb[i] = d[l].idx, na[i] = pd(d[l].left, r);
        else nb[i] = d[r].idx, na[i] = pd(l, d[r].right);
        if(l) d[l].right = r;
        if(r) d[r].left = l;
    }
    for(i = 1; i <= n; i++) {
        f[i][0] = nb[na[i]];
        dpa[i][0] = abs(d[p[i]].val - d[p[na[i]]].val);
        dpb[i][0] = abs(d[p[f[i][0]]].val - d[p[na[i]]].val);
    }
    init();
    scanf("%lld %d", &x, &m);
    for(i = 1; i <= n; i++) {
        solve(i, x);
        if(b && 1.0 * a / b < minv) {
            minv = 1.0 * a / b;
            ans = i;
        }
    }
    printf("%d\n", ans);
    for(i = 1; i <= m; i++) {
        scanf("%d %lld", &j, &x);
        solve(j, x);
        printf("%d %d\n", a, b);
    }
    return 0;
}

D2T1 P1082 同余方程

其实这就是求逆元的模板题啊!(题目保证有解)

同余方程化成不定方程就是\(ax-1=kb\),也就是\(ax-kb=1\)

exgcd求的是\(ax+by=m\),这是最标准的方程形式。

这个方程有解,当且仅当\(m \mod gcd(a,b)=0\)

而因为此处\(m=1\),所以\(gcd(a,b)=1\),即\(ab\)互质。

所以直接按照最标准的exgcd跑一跑,找到那个正的最小的\(x\)即可。

#include<cstdio>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int r = exgcd(b, a %b, x, y);
    int t = x;
    x = y;
    y = t - a / b * y;
    return r;
}
int inverse(int a, int b)
{
    int x, y;
    exgcd(a, b, x, y);
    return (x + b) % b;
}
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);
    printf("%d\n", inverse(a, b));
    return 0;
}

D2T2 P1083 借教室

这道题比D1T2简单多了好吧

我们把这些教室看成一个大区间,那么就变成区间上的操作了。

第一种做法是直接建出线段树,每次借教室直接区间减,当询问到最小值小于0时就停止。

这种做法常数大,远古老爷机一定跑不过的。

第二种是利用题目的单调性进行二分答案,用差分来检验答案。

题目告诉我们一旦有不满足的立即停止,而满足越多越容易不满足。所以就可以二分答案了。

check函数中对每一次借教室只需要前端+1,后端再后的位置-1即可。最后跑一次前缀和就完事了。

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1000005;
int a[maxn], diff[maxn];
struct Node
{
    int u, v, w;
} s[maxn];
int n, m;
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;
}
bool check(int mid)
{
    memset(diff, 0, sizeof(diff));
    for(int i = 1; i <= mid; i++)
    {
        diff[s[i].u] += s[i].w;
        diff[s[i].v + 1] -= s[i].w;
    }
    for(int i = 1; i <= n; i++)
    {
        diff[i] += diff[i - 1];
        if(diff[i] > a[i]) return false;
    }
    return true;
}
int main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int i = 1; i <= m; i++)
    {
        s[i].w = read(), s[i].u = read(), s[i].v = read();
    }
    if(check(m))
    {
        printf("0\n");
        return 0;
    }
    int left = 1, right = m, ans = 0;
    while(left <= right)
    {
        int mid = (left + right) >> 1;
        if(check(mid)) ans = mid, left = mid + 1;
        else right = mid - 1;
    }
    printf("-1\n%d\n", ans + 1);
    return 0;
}

D2T3 P1084 疫情控制

https://www.luogu.org/blog/qzh/p1084-yi-qing-kong-zhi

#include<bits/stdc++.h>

const int maxn = 100005;
int n, m;
struct Edges {
    int next, to, weight;
} e[maxn];
int head[maxn], tot;
int query[maxn], qtot;
int fa[maxn][21], dist[maxn][21];
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 link(int u, int v, int w) {
    e[++tot] = (Edges){head[u], v, w};
    head[u] = tot;
}
void dfs1(int u, int f, int dis) {
    fa[u][0] = f; dist[u][0] = dis;
    for(int j = 1; j <= 20; j++) {
        fa[u][j] = fa[fa[u][j - 1]][j - 1];
        dist[u][j] = dist[u][j - 1] + dist[fa[u][j - 1]][j - 1];
    }
    for(int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if(v == f) continue;
        dfs1(v, u, e[i].weight);
    }
}

struct Nodes {
    int val, idx;
    bool operator < (const Nodes &rhs) const {
        return val < rhs.val;
    }
} s[maxn];//¿ÉÒÔÖ§Ô®±ðÈ˵Ä
int stot;
bool vis[maxn];// ÒѾ­×¤ÔúÁËÂð
bool need[maxn];
int a[maxn], atot;
int b[maxn], btot;

bool dfs(int u) {
    bool isleaf = true;
    if(vis[u]) return true;
    for(int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if(v == fa[u][0]) continue;
        isleaf = false;
        if(!dfs(v)) return false;
    }
    if(isleaf) return false;
    return true;
}
bool check(int mid) {
    // init
    memset(vis, false, sizeof vis);
    memset(need, false, sizeof need);
    stot = atot = btot = 0;
    
    for(int i = 1; i <= m; i++) {
        int now = query[i], dis = 0;
        for(int j = 20; j >= 0; j--) {
            if(fa[now][j] > 1 && dis + dist[now][j] <= mid) {
                dis += dist[now][j]; now = fa[now][j];
            }
        }
        if(fa[now][0] == 1 && dis + dist[now][0] <= mid) {
            s[++stot] = (Nodes){mid - dis - dist[now][0], now};
        } else vis[now] = true;
    }
    
    for(int i = head[1]; i; i = e[i].next) {
        int v = e[i].to;
        if(!dfs(v)) need[v] = true;
    }
    
    std::sort(s + 1, s + stot + 1);
    for(int i = 1; i <= stot; i++) {
        if(need[s[i].idx] && s[i].val < dist[s[i].idx][0]) need[s[i].idx] = false;
        else a[++atot] = s[i].val;
    }
    
    for(int i = head[1]; i; i = e[i].next) {
        int v = e[i].to;
        if(need[v]) {
            b[++btot] = dist[v][0];
        }
    }
    if(atot < btot) return false;
    std::sort(a + 1, a + atot + 1);
    std::sort(b + 1, b + btot + 1);
    int i = 1, j = 1;
    while(i <= btot && j <= atot) {
        if(a[j] >= b[i]) i++, j++;
        else j++;
    }
    if(i > btot) return true;
    return false;
}
int main() {
    int left = 0, right = 0, ans = -1;
    n = read();
    for(int i = 1; i < n; i++) {
        int u = read(), v = read(), w = read();
        link(u, v, w); link(v, u, w);
        right += w;
    }
    dfs1(1, 0, 0);
    m = read();
    for(int i = 1; i <= m; i++) query[i] = read();
    while(left <= right) {
        int mid = (left + right) / 2;
        if(check(mid)) ans = mid, right = mid - 1;
        else left = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Garen-Wang/p/11619064.html