2019 Multi-University Training Contest 9

2019 Multi-University Training Contest 9

B

题意:在 n*m 的矩形上有 k 个平行于坐标轴的射线,求把矩形分成多少块。 \(n,m \le 10^9,K \le 10^5\)

key:线段树

V-E+F=2,即计算点数和边数,用个线段树扫一下。细节麻烦。

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

struct BIT {
    int tree[SZ],n;
    void init(int _n) {
        n = _n;
        n ++;
        for(int i = 1;i <= n;i ++) tree[i] = 0;
    }

    void add(int x,int d) {
        x ++;
        for(int i = x;i <= n;i += i & -i) tree[i] += d;
    }
    int ask(int x) {
        x ++;
        int ans = 0;
        for(int i = x;i > 0;i -= i & -i) ans += tree[i];
        return ans;
    }

    int ask(int l,int r) {
        return ask(r)-ask(l-1);
    }
}tb;

pii X[SZ],Y[SZ];
vector<int> del[SZ],add[SZ];

void work(pii X[],pii Y[],int n,int m,LL &V,LL &E) {
   // puts("-------------------");
    for(int i = 0;i <= m;i ++) del[i].clear(),add[i].clear();
    for(int i = 1;i <= n;i ++) {
        if(X[i].first < X[i].second) {
            del[X[i].first].push_back(i);
            add[X[i].second].push_back(i);
        }
    }
    tb.init(n);
    for(int i = 1;i <= n;i ++) {
        if(X[i].first >= 1) {
            tb.add(i,1);
        }
    }
    for(int i = 1;i <= m;i ++) {
        sort(add[i].begin(),add[i].end());
        sort(del[i].begin(),del[i].end());
        for(int x : add[i]) tb.add(x,1);
        if(Y[i].first < Y[i].second) {
            if(Y[i].first >= 1) {
                int sum = tb.ask(1,Y[i].first);
                V += sum;
                E += sum - 1;
            }
            if(Y[i].second <= n) {
                int sum = tb.ask(Y[i].second,n);
                V += sum;
                E += sum - 1;
            }
        }
        else {
            int t = tb.ask(1,n);
            V += t;
            E += t - 1;
        }
        for(int x : del[i]) tb.add(x,-1);
     //   cout << V << " " << E << endl;
    }
}

int lshx[SZ],lshy[SZ];
pii a[SZ];
char opt[SZ];

int main() {
    int T = read();
    while(T --) {
        int n = read(),m = read(),k = read();
        lshx[0] = lshy[0] = 0;
        for(int i = 1;i <= k;i ++) {
            a[i].first = read();
            a[i].second = read();
            char s[2]; scanf("%s",s);
            opt[i] = s[0];
            lshx[++ lshx[0]] = a[i].first;
            lshy[++ lshy[0]] = a[i].second;
        }
        lshx[++ lshx[0]] = 0; lshx[++ lshx[0]] = n;
        lshy[++ lshy[0]] = 0; lshy[++ lshy[0]] = m;
        sort(lshx+1,lshx+1+lshx[0]); n = unique(lshx+1,lshx+1+lshx[0]) - lshx - 1;
        sort(lshy+1,lshy+1+lshy[0]); m = unique(lshy+1,lshy+1+lshy[0]) - lshy - 1;
        for(int i = 1;i <= k;i ++) {
            a[i].first = lower_bound(lshx+1,lshx+1+n,a[i].first)-lshx;
            a[i].second = lower_bound(lshy+1,lshy+1+m,a[i].second)-lshy;
        //    cout << a[i].first << " "<< a[i].second << endl;
        }

        for(int i = 1;i <= n;i ++) X[i].first = -1,X[i].second = m+1;
        for(int i = 1;i <= m;i ++) Y[i].first = -1,Y[i].second = n+1;
        for(int i = 1;i <= k;i ++) {
            int x = a[i].first,y = a[i].second;
            char s[2]; s[0] = opt[i];
            if(s[0] == 'U') X[x].second = min(X[x].second,y);
            if(s[0] == 'D') X[x].first = max(X[x].first,y);
            if(s[0] == 'L') Y[y].first = max(Y[y].first,x);
            if(s[0] == 'R') Y[y].second = min(Y[y].second,x);
        }
        X[1].first = m; X[1].second = 1; X[n].first = m; X[n].second = 1;
        Y[1].first = n; Y[1].second = 1; Y[m].first = n; Y[m].second = 1;

       // for(int i = 1;i < n;i ++) printf("%d %d\n",X[i].first,X[i].second);
       // for(int i = 1;i < m;i ++) printf("%d %d\n",Y[i].first,Y[i].second);

        LL V = 0,E = 0;
        work(X,Y,n,m,V,E);
        work(Y,X,m,n,V,E);
        V /= 2;
        printf("%lld\n",1+E-V);
    }
}

C

题意:给 n 个数,定义 \(f(x)\) 为数字 4 在 x 的十进制表示中的出现次数。求 \(\sum_{S} f(\sum_{i \in S}a_i)\)\(n \le 40,a_i \le 44444444\)

key:思路

考虑把前一半和后一半的组合情况统计好了,现在问题变成给两个数组,问两边各取一个数字求和的 f 之和。

最大就 10 位,所以考虑按位统计。假设当前考虑第 k 位,那么如果两个数组中的数字按照 mod 10^k 排序,那么 a 数组中的每个元素对应了 b 数组中的两个区间(即相加为 4.... 和 14.....)。

直接做是 \(O(10*2^{n/2}n)\) ,注意到如果 k 从低向高枚举,那么其实是计数排序的过程。如果 a 和 b 都有序,那么 a 中随着 i 的增大,两个区间在 b 中只会向前,可以用双指针来扫。所以复杂度是 \(O(10*2^{n/2})\)

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 2e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

int w[44];

int work(int l,int r,LL a[]) {
    int n = r-l+1,tot = 0;
    for(int S = 0;S < (1<<n);S ++) {
        LL sum = 0;
        for(int i = 0;i < n;i ++) {
            if(S >> i & 1) sum += w[i+l];
        }
        a[++ tot] = sum;
    }
    return tot;
}

int g[10][SZ];

void my_sort(LL a[],int n,int k) {
    for(int i = 0;i < 10;i ++) g[i][0] = 0;
    LL B = 1;
    for(int i = 1;i < k;i ++) B *= 10;
    for(int i = 1;i <= n;i ++) {
        int x = a[i] / B % 10;
        g[x][++ g[x][0]] = a[i];
    }
    int tot = 0;
    for(int i = 0;i < 10;i ++) {
        for(int j = 1;j <= g[i][0];j ++) {
            a[++ tot] = g[i][j];
        }
    }
}

LL a[SZ],b[SZ],c[SZ];

int main() {
    //freopen("test.in","r",stdin); freopen("my.out","w",stdout);
    int T = read();
    while(T --) {
        int K = read();
        for(int i = 1;i <= K;i ++) w[i] = read();
        int n = work(1,K/2,a),m = work(K/2+1,K,b);
        LL B = 1,ans = 0;
        for(int k = 1;k <= 10;k ++,B *= 10) {
            my_sort(a,n,k); my_sort(b,m,k);
           // for(int i = 1;i <= n;i ++) printf("%lld ",a[i]); puts("");
           // for(int i = 1;i <= m;i ++) printf("%lld ",b[i]); puts("");
            for(int i = 1;i <= m;i ++) c[i] = b[i] % (B*10);
            int l1 = m+1,r1 = m;
            int l2 = m+1,r2 = m;
            for(int i = 1;i <= n;i ++) {
                int A = a[i] % (B*10);
                while(l1 >= 2 && A + c[l1-1] >= 4 * B) l1 --;
                while(r1 >= 1 && A + c[r1] >= 5 * B) r1 --;
                while(l2 >= 2 && A + c[l2-1] >= 14 * B) l2 --;
                while(r2 >= 1 && A + c[r2] >= 15 * B) r2 --;
                int t = r1 - l1 + 1 + r2 - l2 + 1;
                if(t) {
                 //   printf("%d : [%d,%d] [%d,%d]\n",i,l1,r1,l2,r2);
                    ans += t;
                }
            }
        }
        printf("%lld\n",ans);
    }
}

D

题意:求序列 \(1,2,...,n\) 的等比子序列个数。 \(n \le 5*10^{17}\)

key:推公式

长度小于等于 2 的直接算,现在看大于 3 的。

假设公比是 \(a/b\) ,满足 a>b 且 gcd(a,b)=1,假设长度为 k ,第一项是 A ,那么最后一项是 \(A\frac{a}{b}^{k-1}\) 。由于这个是整数,那么一定要满足 \(b^{k-1}|A\) 。所以 \(a^{k-1}\) 必是其因子。由于 b<a ,所以确定 a,b,k 和最后一项之后,该等比子序列也随之确定,并且是唯一的。

所以等比数列个数是
\[ \sum_{a=2}^n \phi(a)\lfloor \frac{n}{a^{k-1}} \rfloor \]
k>3时直接暴力,k=3 时可以证明 \(\lfloor \frac{n}{a^2} \rfloor\) 只有三次根号个,所以就数论分块+杜教筛。

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 5e7 + 10;
const int INF = 1e9 + 10;
const int mod = 998244353;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

const int MAXN = 5e7;

bool vis[SZ];
int pri[SZ],tot,phi[SZ],sum_phi[SZ];

void shai(int n) {
    phi[1] = 1;
    for(int i = 2;i <= n;i ++) {
        if(!vis[i]) pri[++ tot] = i,phi[i] = i - 1;
        for(int j = 1,m;j <= tot && (m=i*pri[j]) <= n;j ++) {
            vis[m] = 1;
            if(i%pri[j] == 0) {
                phi[m] = phi[i] * pri[j];
                break;
            }
            else {
                phi[m] = phi[i] * (pri[j] - 1);
            }
        }
    }
    for(int i = 1;i <= n;i ++) sum_phi[i] = (sum_phi[i-1] + phi[i]) % mod;
}

LL f1(LL n) {
    n %= mod;
    return n * (n + 1) % mod * ((mod+1)/2) % mod;
}

unordered_map<LL,LL> mp;

LL dfs(LL n) {
    if(n <= MAXN) return sum_phi[n];
    if(mp.count(n)) return mp[n];
    LL ans = f1(n);
    for(LL i = 2,r;i <= n;i = r + 1) {
        r = n / (n / i);
        (ans -= dfs(n / i) * (r-i+1) % mod) %= mod;
    }
    ans += mod; ans %= mod;
    return ans;
}


LL work3(LL n) {
    LL ans = 0;
    for(LL a = 2,r;a * a <= n;a = r + 1) {
        r = sqrt(n/(n/a/a));
        (ans += (dfs(r) - dfs(a-1)) * ((n/a/a) % mod) % mod) %= mod;
    }
    ans += mod; ans %= mod;
    return ans;
}

LL work0(LL n) {
    LL ans = f1(n);
    for(LL a = 2;;a ++) {
        LL now = a * a;
        if(now > n / a) break;
        now *= a;
        while(1) {
            (ans += phi[a] * ((n / now)%mod) % mod) %= mod;
            if(now > n / a) break;
            now *= a;
        }
    }
    return ans;
}

int main() {
    shai(MAXN);
    int T = read();
    while(T --) {
        LL n = read();
        LL ans3 = work3(n);
        LL ans0 = work0(n);
        LL ans = (ans3 + ans0) % mod;
        printf("%lld\n",ans);
     //   printf("0:%lld 3:%lld\n",ans0,ans3);
    }
}

G

题意:给一棵树,定义(a,b) 合法当且仅当树中存在一条长为 a 的路径和一条长为 b 的路径,且两路径不相交。求合法点对的个数。 \(n \le 10^5\)

key:树的直径,st表

题解做法:直径的两个端点一定都被使用了。对于每个 a 找到最大的 b ,分情况讨论一下,能搞到 O(n)

我的做法:实际上所有的选法一定是【小于等于子树内直径的所有数】和【小于等于子树外直径的所有数】的笛卡尔积。对每个 a 找最大的 b,实际上是一个序列前缀取 max 的操作,操作完后询问每个点的答案,可以用 st 表实现。而这两个东西可以用两遍树形dp实现。

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}
/*
void kaizhan() {
    int size = 256 << 20; // 256MB
    char *p = (char*)malloc(size) + size;
    __asm__("movl %0, %%esp\n" :: "r"(p));
}
*/
int st[SZ][22];
vector<int> G[SZ];

int f[SZ],g[SZ];

void dfs1(int u,int fa) {
    f[u] = 1; g[u] = 1;
    for(int v : G[u]) {
        if(v == fa) continue;
        dfs1(v,u);
        g[u] = max(g[u],f[u] + f[v]);
        f[u] = max(f[u],f[v] + 1);
    }
}

void change_max(int l,int r,int d) {
    if(r<0||d<0) return ;
    int k = log2(r-l+1);
    st[l][k] = max(st[l][k],d);
    st[r-(1<<k)+1][k] = max(st[r-(1<<k)+1][k],d);
}

struct haha {
    set<pii> s;
    void push(int x,int y) {
        s.insert(make_pair(x,y));
    }

    void del(int x,int y) {
        s.erase(make_pair(x,y));
    }

    int find_one_max() {
        if(s.empty()) return -INF;
        return prev(s.end()) -> first;
    }

    int find_two_max() {
        if(s.size() < 2) return -INF;
        return prev(s.end()) -> first + prev(prev(s.end())) -> first;
    }
};

void dfs2(int u,int fa,int w0,int w1) {
    change_max(1,g[u],w1);
    change_max(1,w1,g[u]);
    //printf("%d %d %d\n",u,w0,w1);

    haha s,s2;
    for(int v : G[u]) {
        if(v == fa) continue;
        s.push(f[v],v);
        s2.push(g[v],v);
    }
    for(int v : G[u]) {
        if(v == fa) continue;
        s.del(f[v],v);
        s2.del(g[v],v);
        int ww1 = max(max(max(w1,s.find_two_max()+1),
                      max(max(s.find_one_max()+1,w0),s.find_one_max()+w0)),
                      s2.find_one_max());
        int ww0 = max(w0+1,s.find_one_max()+2);
        dfs2(v,u,ww0,ww1);
        s.push(f[v],v);
        s2.push(g[v],v);
    }
}

int main() {
//    kaizhan(); freopen("test.in","r",stdin); freopen("my.out","w",stdout);
    int T = read();
    while(T --) {
        int n = read();
        for(int i = 1;i <= n;i ++) G[i].clear();
        for(int i = 1;i < n;i ++) {
            int x = read();
            int y = read();
            G[x].push_back(y);
            G[y].push_back(x);
        }
        if(n == 1) { puts("0"); continue; }
        if(n == 2) { puts("1"); continue; }
        for(int j = 0;j <= log2(n);j ++) {
            for(int i = 1;i + (1<<j) - 1 <= n;i ++) {
                st[i][j] = 0;
            }
        }
        int rt;
        for(int i = 1;i <= n;i ++) {
            if(G[i].size() != 1) {
                rt = i;
                break;
            }
        }
       // printf("%d\n",rt);
        dfs1(rt,0);
        //for(int i = 1;i <= n;i ++) printf("%d ",f[i]); puts("");
        dfs2(rt,0,-INF,-INF);
        for(int j = log2(n);j >= 1;j --) {
            for(int i = 1;i + (1<<j) - 1 <= n;i ++) {
                st[i][j-1] = max(st[i][j-1],st[i][j]);
                st[i+(1<<(j-1))][j-1] = max(st[i+(1<<(j-1))][j-1],st[i][j]);
            }
        }
        LL ans = 0;
        for(int i = 1;i <= n;i ++) ans += st[i][0];
       // for(int i = 1;i <= n;i ++) printf("%d ",st[i][0]); puts("");
        printf("%lld\n",ans);
    }
}
/**
233
10
1 2
2 3
3 4
2 5
5 6
6 7
1 8
8 9
9 10
*/

H

题意:给 n 个数 \(a_i\) 和 n 个数 \(b_i\) ,要使两两配对,权值是 \(a_i \ xor \ b_j\) ,求权值和最大是多少。 \(n \le 10^5\)

key:trie,套路

跟今年多校第五场的 B 一模一样,用那个套路的话只需要把找异或最小值改为找最大值即可。

传送门:https://www.cnblogs.com/dqsssss/p/11312849.html

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 1e5 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

const int MAXN = 2 * 31 * SZ;

struct Trie {
    int ch[MAXN][2],sz[MAXN],val[MAXN];
    int Tcnt = 0;

    void insert(pii v) {
        int p = 0;
        for(int i = 29;i >= 0;i --) {
            int c = v.first >> i & 1;
            sz[p] ++;
            if(!ch[p][c]) ch[p][c] = ++ Tcnt;
            assert(Tcnt < 310 * SZ);
            p = ch[p][c];
        }
        sz[p] ++;
        val[p] = v.second;
    }

    pii find_xor_max(int x) {
        int p = 0,ans = 0;
        for(int i = 29;i >= 0;i --) {
            int c = x >> i & 1;
            if(ch[p][c^1] && sz[ch[p][c^1]]) p = ch[p][c^1],ans |= (c^1)<<i;
            else p = ch[p][c],ans |= c<<i;
        }
        assert(sz[p]);
        return make_pair(ans,val[p]);
    }

    void erase(int x) {
        int p = 0;
        for(int i = 29;i >= 0;i --) {
            int c = x >> i & 1;
            sz[p] --;
            p = ch[p][c];
        }
        sz[p] --;
        val[p] = 0;
    }

    void clear() {
        for(int i = 0;i <= Tcnt;i ++) {
            sz[i] = 0;
            val[i] = 0;
            memset(ch[i],0,sizeof ch[i]);
        }
        Tcnt = 0;
    }
}tree[2];

struct haha {
    int v,t,type,id;
};

mt19937 rd(time(0));

int randlr(int l,int r) { return rd()%(r-l+1)+l; }

int main() {
    int T = read();
    while(T --) {
        tree[0].clear();
        tree[1].clear();

        int n;
        n = read();

        vector<haha> V;
        for(int o = 0;o < 2;o ++) {
            map<int,int> mp;
            for(int i = 1;i <= n;i ++) {
                int x = read();
                mp[x] ++;
            }
            for(pii p : mp) {
                V.push_back((haha){p.first,p.second,o,V.size()});
            }
        }
        for(int i = 0;i < V.size();i ++) {
            tree[V[i].type].insert(make_pair(V[i].v,i));
        }

        vector<int> ans,ins; ins.resize(V.size());
        for(int i = 0;i < V.size();i ++) {
            if(V[i].t == 0) continue;
            stack<int> S;
            S.push(i); ins[i] = 1;
            while(S.size()) {
                haha u = V[S.top()]; S.pop(); ins[u.id] = 0;
                pii p = tree[u.type^1].find_xor_max(u.v);
                assert(V[p.second].type != u.type);
                if(!ins[p.second]) {
                    S.push(u.id); S.push(p.second);
                    ins[u.id] = ins[p.second] = 1;
                }
                else {
                    haha v = V[p.second];
                    ins[v.id] = 0; S.pop();
                    tree[u.type].erase(u.v);
                    tree[v.type].erase(v.v);
                    while(V[u.id].t && V[v.id].t) {
                        ans.push_back(u.v^v.v);
                        V[u.id].t --; V[v.id].t --;
                    }
                    if(V[u.id].t) {
                        tree[u.type].insert(make_pair(u.v,u.id));
                    }
                    if(V[v.id].t) {
                        S.push(v.id);
                        ins[v.id] = 1;
                        tree[v.type].insert(make_pair(v.v,v.id));
                    }
                }
            }
        }
        LL sum = 0;
        for(int i = 0;i < ans.size();i ++) {
            sum += ans[i];
        }
        printf("%lld\n",sum);
    }
    return 0;
 }

猜你喜欢

转载自www.cnblogs.com/dqsssss/p/11397751.html