【训练】9.13 训练赛

A. HDU 6230

一个合法的子串(s = 3n - 2)满足条件即1-2n-1 为以n为回文中心的回文串,n-3n-2为以2n-1为中心的回文串。故我们可以通过寻找回文中心对,来判断相应合法子串的个数。利用manacher求出每个位置的最长回文半径,则若i,j满足条件(i < j) ,则应有 \(p[i] \geqslant  j - i + 1\), \(p[j] \geqslant  j - i + 1\)。变换一下有 \(j \leqslant p[i] + i + 1\),\(j - p[j] + 1 \geqslant i < j\)。所以扫描第一维,然后树状数组区间查询第二维即可。

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define int long long
#define lowbit(i) (i & (-i))
int n, ans, C[maxn], pld[maxn];
char s[maxn];

int read() {
    int x = 0, k = 1; 
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

struct node {
    int num, id;
    friend bool operator <(node x, node y) {
        return x.num > y.num;
    }
}b[maxn];

void Add(int x, int y) {
    for(int i = x; i <= n; i += lowbit(i)) C[i] += y;
}

int Que(int x) {
    int ans = 0;
    for(int i = x; i; i -= lowbit(i)) ans += C[i];
    return ans;
}

void Manacher() {
    int mx = 0, id = 0;
    for(int i = 1; i <= n; i ++) {
        pld[i] = 0;
        if(mx >= i) {
            int j = 2 * id - i; 
            pld[i] = min(pld[j], mx - i + 1);
        }
        else pld[i] = 1;
        int r = min(n - i, i - 1);
        while(pld[i] <= r && s[i + pld[i]] == s[i - pld[i]]) pld[i] ++;
        if(i + pld[i] - 1 > mx) mx = i + pld[i] - 1, id = i;
    }
}

signed main() {
    int T = read();
    while(T --) {
        scanf("%s", s + 1); n = strlen(s + 1), ans = 0;
        Manacher();
        for(int i = 1; i <= n; i ++) C[i] = 0;
        for(int i = 1; i <= n; i ++) b[i].num = pld[i] + i - 1, b[i].id = i;
        sort(b + 1, b + 1 + n);
        for(int i = n, j = 1; i >= 1; i --) {
            while(j <= n && b[j].num >= i) Add(b[j].id, 1), j ++;
            ans += Que(i - 1) - Que(i - pld[i]); 
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 

B.HDU 6231

二分答案。设二分的答案为mid,则考虑统计第k大数大于等于mid的区间共有多少个。把所有大于等于mid的数看做有效数字,等价于统计其中含有至少k个有效数字的区间的个数。若此时区间个数少于s,说明答案应当减小。若大于等于,则增大答案继续寻找。

#include <bits/stdc++.h>
using namespace std;
#define maxn 200000
#define int long long
#define INF 2000000000LL
int n, k, m, a[maxn], b[maxn], sum[maxn];

int read() {
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

bool Check(int mid) {
    int ans = 0;
    for(int i = 1; i <= n; i ++) 
        if(a[i] >= mid) b[i] = 1;
        else b[i] = 0;
    for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + b[i];
    for(int i = 1, j = 0; i <= n; i ++) {
        while(j < n && sum[i] - sum[j] >= k) j ++;
        ans += j;
    }
    return ans < m;
}

signed main() {
    int T = read();
    while(T --) {
        n = read(), k = read(), m = read();
        int l = INF, r = -INF;
        for(int i = 1; i <= n; i ++) 
            a[i] = read(), l = min(l, a[i]), r = max(r, a[i]);
        while(l < r) {
            int mid = (l + r) >> 1;
            if(mid + 1 <= r) mid += 1;
            if(Check(mid)) r = mid - 1;
            else l = mid;
        }
        printf("%lld\n", l);
    }
    return 0;
}

F. HDU6235

签到题。

#include <bits/stdc++.h>
using namespace std;
#define maxn 200000
#define int long long
#define INF 2000000000LL
int n, k, m, a[maxn], b[maxn], sum[maxn];

int read() {
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

bool Check(int mid) {
    int ans = 0;
    for(int i = 1; i <= n; i ++) 
        if(a[i] >= mid) b[i] = 1;
        else b[i] = 0;
    for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + b[i];
    for(int i = 1, j = 0; i <= n; i ++) {
        while(j < n && sum[i] - sum[j] >= k) j ++;
        ans += j;
    }
    return ans < m;
}

signed main() {
    int T = read();
    while(T --) {
        n = read(), k = read(), m = read();
        int l = INF, r = -INF;
        for(int i = 1; i <= n; i ++) 
            a[i] = read(), l = min(l, a[i]), r = max(r, a[i]);
        while(l < r) {
            int mid = (l + r) >> 1;
            if(mid + 1 <= r) mid += 1;
            if(Check(mid)) r = mid - 1;
            else l = mid;
        }
        printf("%lld\n", l);
    }
    return 0;
}

H.HDU6237

首先发现最后的x一定是石子总个数的质因子之一,而质因子总数最多不超过20。枚举每一个质因子,将石堆石子个数对 x 取模。之后排序,最优方案一定是把模意义下个数小的石堆的石子往模意义下个数大的石堆上挪。

#include <bits/stdc++.h>
using namespace std;
#define maxn 500000
#define MAXN 500000
#define INF 99999999999999LL
#define LL long long
#define int long long 
int n, tot, cnt, p[maxn], pri[maxn], a[maxn], b[maxn];
LL sum, ans;
bool is_pri[maxn];

int read() {
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

void Get_Prime() {
    for(int i = 2; i < MAXN; i ++) {
        if(!is_pri[i]) pri[++ tot] = i;
        for(int j = 1; j <= tot; j ++) {
            if(i * pri[j] > MAXN) break;
            is_pri[i * pri[j]] = 1;
            if(!(i % pri[j])) break;
        }
    }
}

bool Check(LL x) {
    if(x == 1) return 0;
    int X = sqrt(x);
    for(int i = 2; i <= X; i ++)
        if(!(x % i)) return 0;
    return 1;
}

LL Solve(int p) {
    for(int i = 1; i <= n; i ++) b[i] = a[i] % p;
    sort(b + 1, b + 1 + n);
    LL s = 0, w = 0;
    for(int i = 1, j = n; i <= n && i < j; i ++) {
        w += b[i]; s += b[i];
        while(b[j]) {
            int t = p - b[j];
            if(s >= t) b[j] = p, j --, s -= t;
            else {
                b[j] += s, s = 0; 
                break;
            }
        }
    }
    return w;
}

signed main() {
    int T = read();
    Get_Prime();
    while(T --) {
        n = read(); ans = INF; sum = 0, cnt = 0;
        for(int i = 1; i <= n; i ++) a[i] = read(), sum += (LL) a[i];
        int N = sqrt(sum);
        for(int i = 2; i <= N; i ++) 
            if(!is_pri[i] && !(sum % i)) {
                p[++ cnt] = i;
                while(!(sum % i)) sum /= i;
            }    
        if(Check(sum)) p[++ cnt] = sum;
        for(int i = 1; i <= cnt; i ++) {
            ans = min(ans, Solve(p[i]));
        }
        printf("%lld\n", ans);
    }
    return 0;
}

L.HDU6241

第一个条件子树中至少涂几个点,第二个条件子树外至少涂几个点。二分答案后,这两个条件转化为子树内至少涂几个点,至多涂几个点。树形向上整合区间取交,判断是否出现空集。

#include <bits/stdc++.h>
using namespace std;
#define maxn 500000
int n, MID, num[maxn], dp[maxn], size[maxn], a[maxn], b[maxn];
int L[maxn], R[maxn];
bool flag;

int read() {
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

struct edge {
    int head[maxn], to[maxn], last[maxn], cnp = 1;
    void add(int u, int v) {
        to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++;
        to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++;
    }
}E;

void dfs(int u, int fa) {
    size[u] = 1;
    for(int i = E.head[u]; i; i = E.last[i]) {
        int v = E.to[i];
        if(v == fa) continue;
        dfs(v, u);
        size[u] += size[v];
    }
}

void DP(int u, int fa) {
    L[u] = 0, R[u] = 1;
    for(int i = E.head[u]; i; i = E.last[i]) {
        int v = E.to[i];
        if(v == fa) continue;
        DP(v, u);
        R[u] += R[v];
        L[u] += L[v];
    }
    R[u] = min(R[u], MID - b[u]);
    L[u] = max(L[u], a[u]);
    if(L[u] > R[u] || n - size[u] < b[u]) flag = 0;
}

bool Check(int mid) {
    flag = 1, MID = mid;
    DP(1, 0);
    if(L[1] > mid || R[1] < mid) flag = 0;
    return flag;
}

int main() {
    int T = read(); 
    while(T --) {
        n = read(); E.cnp = 1;
        for(int i = 1; i <= n; i ++) E.head[i] = a[i] = b[i] = 0;
        for(int i = 1; i < n; i ++) {
            int u = read(), v = read();
            E.add(u, v); 
        }
        int A = read();
        for(int i = 1; i <= A; i ++) {
            int x = read(), y = read();
            a[x] = max(a[x], y);
        }
        int B = read();
        for(int i = 1; i <= B; i ++) {
            int x = read(), y = read();
            b[x] = max(b[x], y);
        }
        dfs(1, 0);
        int l = 1, r = n;
        while(l < r) {
            int mid = (l + r) >> 1;
            if(Check(mid)) r = mid;
            else l = mid + 1;
        }
        if(Check(0)) printf("0\n");
        else if(Check(l)) printf("%d\n", l);
        else printf("-1\n");
    }
    return 0;
}

 M.HDU6242

三点确定一个圆,由于有一半的点都在一个圆上,因此每次随机三个点找到圆的概率很高。

【一直TLE | WA】咕咕咕

posted @ 2020-09-28 09:24  Twilight_Sx  阅读(122)  评论(0编辑  收藏  举报