20200405~06题解

Day1:

第一题:水题

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 1005;
char s1[MAXN], s2[MAXN]; 

inline int ord(char c){
    if(c >= 'a' && c <= 'z') return c - 'a';
    return c - 'A';
}

int main(){
    scanf("%s%s", s1, s2);
    int len1 = strlen(s1), len2 = strlen(s2);
    for(int i=0, j=0; i<len2; i++, j=(j+1)%len1)
        putchar((26 + ord(s2[i]) - ord(s1[j])) % 26 + ((s2[i] >= 'a') ? 'a' : 'A'));
    return 0;
}
View Code

第二题:贪心提前处理出最后的序列(以$a*b$为关键字排序),最后高精度模拟即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 1005;
struct Node{
    int a, b;
    const bool operator < (const Node &x) const{
        return a * b < x.a * x.b;
    }
}a[MAXN];

const int BITLEN = 10000, BIGSIZE = 900;
struct Big{
    int val[BIGSIZE+1];
    void operator = (int x){
        memset(val, 0, sizeof(val));
        val[0] = x;
    }
    Big(int x = 0){this->operator = (x);}
    Big operator + (Big x){
        Big ans;
        for(int i=0; i<BIGSIZE; i++){
            ans.val[i] += val[i] + x.val[i];
            ans.val[i + 1] += ans.val[i] / BITLEN;
            ans.val[i] %= BITLEN;
        }
        return ans;
    }
    Big operator * (int x){
        Big ans;
        for(int i=0; i<BIGSIZE; i++)
            ans.val[i] = x * val[i];
        for(int i=0; i<BIGSIZE-1; i++)
            ans.val[i+1] += ans.val[i] / BITLEN, ans.val[i] %= BITLEN;
        return ans;
    }
    Big operator / (int x){
        Big ans;
        for(int i=BIGSIZE-1, rest=0; i>=0; i--){
            rest = rest * BITLEN + val[i];
            ans.val[i] = rest / x;
            rest = rest % x;
        }
        return ans;
    }
    const bool operator < (const Big &x)const{
        for(int i=BIGSIZE-1; i>=0; i--)
            if(val[i] != x.val[i]) return val[i] < x.val[i];
        return false;
    }
    void print(){
        int len = BIGSIZE - 1;
        while(!val[len]) len--;
        for(int i=len; i>=0; i--){
            if(i != len)
                for(int j=BITLEN/10; j>1; j/=10)
                    if(val[i] < j)
                        putchar('0');
            printf("%d", val[i]);
        }
    }
};

int main(){
    int n = read();
    for(int i=0; i<=n; i++)
        a[i].a = read(), a[i].b = read();
    sort(a+1, a+1+n);
    Big ans = 0, sum = a[0].a;
    for(int i=1; i<=n; i++){
        Big temp = sum / a[i].b;
        if(ans < temp) ans = temp;
        sum = sum * a[i].a;
    }
    ans.print();
    return 0;
}
View Code

第三题:倍增Dp提前预处理出一个点走$2^i$后的情况,对于寻找第一近和第二近的房子可以用map进行维护,每次找到最近的几个点,最后直接模拟即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <queue>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 100001, LOGN = 18;
int h[MAXN], next1[MAXN], next2[MAXN];
long long Dp[MAXN][LOGN+1][2], last[MAXN][LOGN+1];
map<int,int> hash;

struct Tuple{
    int dis, h, id;
    const bool operator < (const Tuple &x) const{
        if(dis != x.dis) return dis > x.dis;
        return h > x.h;
    }
};

priority_queue<Tuple> Q;

int main(){
    int n = read();
    for(int i=1; i<=n; i++)
        h[i] = read();
    hash[h[n]] = n, hash[h[n-1]] = n - 1;
    next1[n] = next2[n] = n, next1[n-1] = n, next2[n-1] = n-1;
    for(int i=n-2; i>=1; i--){
        map<int,int>::iterator it = hash.upper_bound(h[i]), it2 = it;
        for(int j=1; j<=2; j++, it++) if(it != hash.end())
            Q.push((Tuple){abs(h[i]-it->first), it->first, it->second});
        if(it2 != hash.begin()){
            it2--;
            for(int j=1; j<=2; j++, it2--){
                Q.push((Tuple){abs(h[i]-it2->first), it2->first, it2->second});
                if(it2 == hash.begin()) break;
            }
        }
        next1[i] = Q.top().id;
        Q.pop();
        next2[i] = Q.top().id;
        hash[h[i]] = i;
        while(!Q.empty()) Q.pop();
    }
    for(int i=1; i<=n; i++)
        Dp[i][0][0] = abs(h[next2[i]] - h[i]), Dp[i][0][1] = abs(h[next1[i]] - h[i]);
    for(int i=1; i<=n; i++)
        Dp[i][1][0] = Dp[i][0][0], Dp[i][1][1] = (next2[i] == i) ? 0 : Dp[next2[i]][0][1], last[i][1] = (next2[i] == i) ? next2[i] : next1[next2[i]];
    for(int i=2; i<=LOGN; i++) for(int j=1; j<=n; j++){
        Dp[j][i][0] = Dp[last[j][i-1]][i-1][0] + Dp[j][i-1][0], Dp[j][i][1] = Dp[last[j][i-1]][i-1][1] + Dp[j][i-1][1];
        last[j][i] = last[last[j][i-1]][i-1];
    }
    int x0 = read(), s0 = 0;
    double maxNum = 1e18;
    for(int i=1; i<=n; i++){
        long long disA = 0, disB = 0, u = i, x = x0;
        for(int j=LOGN; j>=1; j--) if(Dp[u][j][0] + Dp[u][j][1] <= x)
            disA += Dp[u][j][0], disB += Dp[u][j][1], x -= Dp[u][j][0] + Dp[u][j][1], u = last[u][j];
        if(Dp[u][0][0] <= x) disA += Dp[u][0][0];
        if((double)disA / disB < maxNum) maxNum = (double)disA / disB, s0 = i;
    }
    printf("%d
", s0);
    int m = read();
    for(int i=1; i<=m; i++){
        long long disA = 0, disB = 0, u = read(), x = read();
        for(int j=LOGN; j>=1; j--) if(Dp[u][j][0] + Dp[u][j][1] <= x)
            disA += Dp[u][j][0], disB += Dp[u][j][1], x -= Dp[u][j][0] + Dp[u][j][1], u = last[u][j];
        if(Dp[u][0][0] <= x) disA += Dp[u][0][0];
        printf("%lld %lld
", disA, disB);
    }
    return 0;
}
View Code

第四题:对于$n<=1000$,暴力即可,对于$n<=10^6,z=0$,使用树状数组优化即可,对于数据加强的$11,12$点,可以考虑用树套树,但也可以用CDQ

因为CDQ的前提必须是更新过的节点不再更新,所以先分治,后计算左对右贡献的做法显然不行,但是可以先分治左,再计算左对右,再计算右,需要时刻牢记自己定义的CDQ是什么

这道题CDQ函数定义:将L到R的已经对X排序的数组计算贡献,并最后按照Y进行排序。

这样如果数组顺序不对需要重新排序(用sort排序),当然因为sort是在第一层CDQ里面的所以总时间复杂度还是$O(nlog^2n)$,和树套树的时间复杂度一样(虽然常数大了一点)

普通代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 1000001;
struct Tuple{
    int x, y, z, val;
    bool const operator < (const Tuple &temp) const{
        if(x != temp.x) return x < temp.x;
        if(y != temp.y) return y < temp.y;
        return z < temp.z;
    }
}tup[MAXN];

struct BIT{
    int val[MAXN];
    void add(int pos,int x){
        for(int i=pos; i<MAXN; i+=i&(-i))
            val[i] = max(val[i], x);
    }
    int query(int pos){
        int ans = 0;
        for(int i=pos; i; i-=i&(-i))
            ans = max(ans, val[i]);
        return ans;
    }
}bit;

int main(){
    int n = read();
    for(int i=1; i<=n; i++)
        tup[i].x = read(), tup[i].y = read(), tup[i].z = read(), tup[i].val = 1;
    sort(tup+1, tup+1+n);
    if(n > 10001){
        for(int i=1; i<=n; i++){
            tup[i].val = bit.query(tup[i].y) + 1;
            bit.add(tup[i].y, tup[i].val);
        }
    }
    else{
        for(int i=1; i<=n; i++) for(int j=1; j<i; j++)
            if(tup[j].y <= tup[i].y && tup[j].z <= tup[i].z)
                tup[i].val = max(tup[i].val, tup[j].val + 1);
    }
    int ans = 0;
    for(int i=1; i<=n; i++)
        ans = max(ans, tup[i].val);
    printf("%d", ans);
    return 0;
}
View Code

数据加强版代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 1000005;
struct Tuple{
    int x, y, z, val;
    bool const operator < (const Tuple &temp) const{
        if(x != temp.x) return x < temp.x;
        if(y != temp.y) return y < temp.y;
        return z < temp.z;
    }
}tup[MAXN], temp[MAXN];

struct BIT{
    int val[MAXN];
    void add(int pos,int x){
        for(int i=pos; i<MAXN; i+=i&(-i))
            val[i] = max(val[i], x);
    }
    int query(int pos){
        int ans = 0;
        for(int i=pos; i; i-=i&(-i))
            ans = max(ans, val[i]);
        return ans;
    }
    void clear(int pos){
        for(int i=pos; i<MAXN; i+=i&(-i))
            val[i] = 0;
    }
}bit;

const bool cmp(const Tuple &a,const Tuple &b){
    if(a.y != b.y) return a.y < b.y;
    return a.z < b.z;
}

void CDQ(int l,int r){
    int mid = (l + r) >> 1;
    if(l == r) return;
    CDQ(l, mid);
    sort(tup+mid+1, tup+r+1, cmp);
    for(int i=l, j=mid+1, k=l; k<=r; k++){
        if(j > r || (i <= mid && tup[i].y <= tup[j].y))
            bit.add(tup[i].z, tup[i].val), i++;
        else tup[j].val = max(tup[j].val, bit.query(tup[j].z) + 1), j++;
    }
    for(int i=l; i<=mid; i++)
        bit.clear(tup[i].z);
    sort(tup+mid+1, tup+r+1);
    CDQ(mid+1, r);
    for(int i=l, j=mid+1, k=l; k<=r; k++){
        if(j > r || (i <= mid && tup[i].y <= tup[j].y))
            temp[k] = tup[i++];
        else temp[k] = tup[j++];
    }
    for(int i=l; i<=r; i++)
        tup[i] = temp[i];
}

int main(){
    int n = read();
    for(int i=1; i<=n; i++)
        tup[i].x = read() + 1, tup[i].y = read() + 1, tup[i].z = read() + 1, tup[i].val = 1;
    sort(tup+1, tup+1+n);
    CDQ(1, n);
    int ans = 0;
    for(int i=1; i<=n; i++)
        ans = max(ans, tup[i].val);
    printf("%d", ans);
    return 0;
}
View Code

 Day2:

第一题:数论水题,恰好用上了我改进版的扩展欧几里得函数

第二题:线段树区间修改,区间查询的题目,当然也可以用前缀数组加上二分答案来做

线段树代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 1000005;

int val[MAXN];

struct Segment{
    struct Node{
        int l, r, isSigned, sign, minNum;
    }node[MAXN*4];
    inline void mix(int x,int sign){
        node[x].sign += sign;
        node[x].minNum += sign;
        node[x].isSigned = true;
    }
    void spread(int x){
        if(node[x].isSigned){
            mix(x*2, node[x].sign);
            mix(x*2+1, node[x].sign);
            node[x].sign = 0;
            node[x].isSigned = false;
        }
    }
    bool change(int l,int r,int sign,int x = 1){
        if(l <= node[x].l && node[x].r <= r){
            mix(x, sign);
            return node[x].minNum >= 0;
        }
        spread(x);
        bool flag = true;
        if(node[x*2].r >= l) flag &= change(l, r, sign, x*2);
        if(node[x*2+1].l <= r) flag &= change(l, r, sign, x*2+1);
        node[x].minNum = min(node[x*2].minNum, node[x*2+1].minNum);
        return flag;
    }
    void build(int l,int r,int x = 1){
        node[x].l = l, node[x].r = r;
        if(l == r){
            node[x].minNum = node[x].sign = val[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, x*2);
        build(mid+1, r, x*2+1);
        node[x].minNum = min(node[x*2].minNum, node[x*2+1].minNum);
    }
}T;

int main(){
    int n = read(), m = read();
    for(int i=1; i<=n; i++)
        val[i] = read();
    T.build(1, n);
    for(int i=1; i<=m; i++){
        int v = read(), l = read(), r = read();
        if(!T.change(l, r, -v)){
            printf("-1
%d", i);
            return 0;
        }
    }
    printf("0");
    return 0;
}
View Code

前缀数组二分答案做法:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 1000005;
long long val[MAXN], temp[MAXN];
int Qv[MAXN], Ql[MAXN], Qr[MAXN];

int main(){
    int n = read(), m = read();
    for(int i=1; i<=n; i++)
        val[i] = read();
    for(int i=1; i<=m; i++)
        Qv[i] = read(), Ql[i] = read(), Qr[i] = read();
    int l = 1, r = m + 1;
    while(l < r){
        int mid = (l + r) >> 1;
        for(int i=1; i<=n; i++)
            temp[i] = val[i];
        for(int i=n; i>=2; i--)
            temp[i] -= temp[i-1];
        for(int i=1; i<=mid; i++)
            temp[Ql[i]] += -Qv[i], temp[Qr[i] + 1] -= -Qv[i];
        for(int i=2; i<=n; i++)
            temp[i] += temp[i-1];
        bool flag = true;
        for(int i=1; i<=n; i++)
            flag &= (temp[i] >= 0);
        if(flag) l = mid + 1;
        else r = mid;
    }
    if(l == m + 1) printf("0");
    else printf("-1
%d", l);
    return 0;
}
View Code

第三题:图论题,首先使用倍增优化优化提点的过程,之后考虑贪心,二分答案,对答案$mid$

将所有的军队分为三类:

1.不能走到1节点,那就尽可能向上走

2.能走到1节点但不能走回来,这样如果这个子节点需要军队驻扎就驻扎在这里,否则就走到1节点

3.能走到1节点,还能走回来,这种军队就走到1节点

最后再对没走到的子节点进行分配即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 50001, LOGN = 16;
int head[MAXN], next[MAXN<<1], last[MAXN<<1], val[MAXN<<1], lineNum = 0;
void add(int x,int y,int v){
    lineNum++, next[lineNum] = head[x], last[lineNum] = y, val[lineNum] = v, head[x] = lineNum;
}

long long Dp[MAXN][LOGN+1], fa[MAXN][LOGN+1];
int pos[MAXN], n, m, cnt = 0;
pair<long long,int> temp[MAXN], need[MAXN];
bool isRest[MAXN], isNeed[MAXN];

void DFS(int x,int pre){
    for(int i=1; i<=LOGN; i++)
        Dp[x][i] = Dp[fa[x][i-1]][i-1] + Dp[x][i-1], fa[x][i] = fa[fa[x][i-1]][i-1];
    for(int l=head[x]; l; l=next[l]){
        int y = last[l];
        if(y != pre)
            Dp[y][0] = val[l], fa[y][0] = x, DFS(y, x);
    }
}

bool BFS(int x,int pre){
    bool isSon = true, flag = false;
    for(int l=head[x]; l; l=next[l]){
        int y = last[l];
        if(y == pre) continue;
        isSon = false, BFS(y, x);
        flag |= isNeed[y];
    }
    if(!isSon) isNeed[x] &= flag;
}

bool check(long long mid){
    memset(isNeed, true, sizeof(isNeed));
    memset(isRest, false, sizeof(isRest));
    for(int i=1; i<=m; i++)
        temp[i] = make_pair(mid, pos[i]);
    for(int i=1; i<=m; i++) for(int j=LOGN; j>=0; j--)
        if(fa[temp[i].second][j] != 1 && temp[i].first >= Dp[temp[i].second][j])
            temp[i].first -= Dp[temp[i].second][j], temp[i].second = fa[temp[i].second][j];
    int cntRest = m, cntLast = 0;
    for(int i=1; i<=m; i++)
        if(temp[i].first < Dp[temp[i].second][0])
            temp[i].first = 1e18, cntRest--, isNeed[temp[i].second] = false;
    for(int i=1; i<=cnt; i++) BFS(need[i].second, 1);
    cntLast = cntRest;
    sort(temp+1, temp+1+m);
    for(int i=1; i<=cntRest; i++)
        if(temp[i].first < 2 * Dp[temp[i].second][0] && isNeed[temp[i].second])
            temp[i].first = 1e18, cntLast--, isNeed[temp[i].second] = false;
        else temp[i].first -= Dp[temp[i].second][0];
    sort(temp+1, temp+1+cntRest);
    cntRest = cntLast;
    int i = 1, j = 1;
    while(1){
        while(!isNeed[need[j].second] && j<=cnt) j++;
        if(j > cnt) return true;
        if(i > cntRest) return false;
        if(temp[i].first < need[j].first) i++;
        else i++, j++;
    }
}

int main(){
    n = read();
    long long l = 0, r = 0;
    for(int i=1; i<n; i++){
        int x = read(), y = read(), v = read();
        add(x, y, v), add(y, x, v), r += v;
    }
    for(int i=0; i<=LOGN; i++) fa[1][i] = 1;
    DFS(1, 1);
    for(int i=2; i<=n; i++) if(fa[i][0] == 1)
        need[++cnt] = make_pair(Dp[i][0], i);
    sort(need+1, need+1+cnt);
    m = read();
    for(int i=1; i<=m; i++)
        pos[i] = read();
    bool flag = false;
    while(l < r){
        int mid = (l + r) >> 1;
        if(check(mid)) r = mid, flag = true;
        else l = mid + 1;
    }
    if(flag) printf("%d", l);
    else printf("-1");
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/PHDHD/p/12637616.html