BZOJ 5月月赛 ADG题解

题目链接

BZOJ5月月赛

题解

好弱啊QAQ只写出三题

A

判断多干个数乘积是否是某个数的倍数有很多方法,比较常用的是取模,但这里并不适用,因为模数不定
会发现数都比较小,所以我们可以考虑分解质因子,查找一下区间各个质因子数是否符合要求
用主席树维护即可
由于\(10^5\)以内不同质因子数最多的也就是\(6\)个,预处理一下质因子,可以看做一个常数
复杂度是\(O(n\sqrt{n} + nlogn)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 100005,maxm = 10000005,N = 100000,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int fac[maxn][20],num[maxn][20],fi[maxn],M;
int p[maxn],pi,isn[maxn];
void init(){
    for (int i = 2; i <= N; i++){
        if (!isn[i]) p[++pi] = i;
        for (int j = 1; j <= pi && i * p[j] <= N; j++){
            isn[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
    for (int i = 2; i <= N; i++){
        int x = i,t;
        for (int j = 1; j <= pi && p[j] * p[j] <= x; j++){
            if (x % p[j] == 0){
                t = ++fi[i];
                fac[i][t] = j;
                while (x % p[j] == 0) num[i][t]++,x /= p[j];
            }
        }
        if (x - 1){
            fac[i][++fi[i]] = lower_bound(p + 1,p + 1 + pi,x) - p;
            num[i][fi[i]] = 1;
        }
    }
    M = pi;
}
int sum[maxm],ls[maxm],rs[maxm],rt[maxn],A[maxn],n,m,cnt;
void add(int& u,int v,int l,int r,int pos,int w){
    sum[u = ++cnt] = sum[v] + w;
    ls[u] = ls[v]; rs[u] = rs[v];
    if (l == r) return;
    int mid = l + r >> 1;
    if (mid >= pos) add(ls[u],ls[v],l,mid,pos,w);
    else add(rs[u],rs[v],mid + 1,r,pos,w);
}
int query(int u,int v,int l,int r,int pos){
    if (!u) return 0;
    if (l == r) return sum[u] - sum[v];
    int mid = l + r >> 1;
    if (mid >= pos) return query(ls[u],ls[v],l,mid,pos);
    return query(rs[u],rs[v],mid + 1,r,pos);
}
int main(){
    init();
    int T = read(),x,l,r,flag;
    while (T--){
        cnt = 0;
        n = read(); m = read();
        REP(i,n){
            x = A[i] = read(); rt[i] = rt[i - 1];
            for (int j = 1; j <= fi[x]; j++)
                add(rt[i],rt[i],1,M,fac[x][j],num[x][j]);
        }
        while (m--){
            l = read(); r = read(); x = read(); flag = true;
            for (int i = 1; i <= fi[x]; i++)
                if (query(rt[r],rt[l - 1],1,M,fac[x][i]) < num[x][i]){
                    flag = false; break;
                }
            if (!flag) puts("No");
            else puts("Yes");
        }
    }
    return 0;
}

D

D是树上两点间的询问,考虑使用树上权值主席树
由于只考虑奇偶性,所以我们覆给每个位置\(1\)\(2\),修改就翻转一下
如何查询?
拿出\(u\)\(v\)所对应的两棵树,我们需要快速合并树上信息得出第一个偶数位置
容易知道当两个位置奇偶性不同时相加起来才是奇数,所以我们只需要找到两棵树第一个相同的位置
权值不同不好比较,那我们就再维护一个值,与原来的值相反,这样转化成了查找第一个不同的位置
我们只需快速比较两个区间是否相同,使用\(hash\)即可
两个树实际上对应两条到根的链,相交的部分除了\(lca\)处都应减去,由于只考虑奇偶性,所以对答案没有影响,所以只用把\(lca\)处的值修改一下即可进行比较
复杂度\(O(nlogn)\),还要优化一下常数,比如只建一次树,\(RMQ\)\(lca\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (register int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (register int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
#define ULL unsigned long long int
using namespace std;
const int maxn = 200005,maxm = 13000005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int h[maxn],ne = 1;
struct EDGE{int to,nxt;}ed[maxn << 1];
inline void build(int u,int v){
    ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
ULL pw[maxn],val[maxm],vv[maxm];
int A[maxn],fa[maxn],dep[maxn],dfn[maxn],mn[maxn << 1][19],Log[maxn << 1],bin[50],tot;
int rt[maxn],ls[maxm],rs[maxm],cnt,n,m,N = 200001,typ,pos,V;
inline void upd(int u,int l,int r){
    val[u] = val[ls[u]] * pw[r - (l + r >> 1)] + val[rs[u]];
    vv[u] = vv[ls[u]] * pw[r - (l + r >> 1)] + vv[rs[u]];
}
void build(int& u,int l,int r){
    u = ++cnt;
    if (l == r){val[u] = 1; vv[u] = 2; return;}
    int mid = l + r >> 1;
    build(ls[u],l,mid);
    build(rs[u],mid + 1,r);
    upd(u,l,r);
}
void modify(int& u,int v,int l,int r){
    u = ++cnt; ls[u] = ls[v]; rs[u] = rs[v];
    if (l == r){val[u] = val[v] == 1 ? 2 : 1; vv[u] = vv[v] == 1 ? 2 : 1; return;}
    int mid = l + r >> 1;
    if (mid >= pos) modify(ls[u],ls[v],l,mid);
    else modify(rs[u],rs[v],mid + 1,r);
    upd(u,l,r);
}
int query(int u,int v,int l,int r){
    if (l == r) return l;
    int mid = l + r >> 1;
    ULL t1 = val[ls[u]],t2 = vv[ls[v]];
    if (pos >= l && pos <= mid){
        if (V == 1) t1 += pw[mid - pos];
        else t1 -= pw[mid - pos];
    }
    if (t1 != t2) return query(ls[u],ls[v],l,mid);
    return query(rs[u],rs[v],mid + 1,r);
}
inline int ask(int u){
    int l = 1,r = N,mid;
    while (l < r){
        mid = l + r >> 1;
        if (mid >= pos) r = mid,u = ls[u];
        else l = mid + 1,u = rs[u];
    }
    return val[u];
}
void dfs(int u){
    pos = A[u]; mn[++tot][0] = u; dfn[u] = tot;
    modify(rt[u],rt[fa[u]],1,N);
    Redge(u) if ((to = ed[k].to) != fa[u]){
        fa[to] = u; dep[to] = dep[u] + 1;
        dfs(to);
        mn[++tot][0] = u;
    }
}
inline int lca(int u,int v){
    int l = dfn[u],r = dfn[v];
    if (l > r) swap(l,r);
    int t = Log[r - l + 1];
    return dep[mn[l][t]] < dep[mn[r - bin[t] + 1][t]] ? mn[l][t] : mn[r - bin[t] + 1][t];
}
/*void print(int u,int l,int r){
    if (l == r){
        printf("%lld ",val[u]);
        return;
    }
    int mid = l + r >> 1;
    print(ls[u],l,mid);
    if (6 > mid)print(rs[u],mid + 1,r);
}*/
int main(){
    bin[0] = 1; for (int i = 1; i <= 25; i++) bin[i] = bin[i - 1] << 1;
    Log[0] = -1; for (register int i = 1; i < (maxn << 1); i++) Log[i] = Log[i >> 1] + 1;
    pw[0] = 1; for (register int i = 1; i < maxn; i++) pw[i] = pw[i - 1] * 5;
    build(rt[0],1,N);
    int tmp = cnt;
    int T = read();
    while (T--){
        n = read(); m = read();
        REP(i,n) h[i] = 0;
        tot = 0; cnt = tmp; ne = 1;
        REP(i,n) A[i] = read();
        for (register int i = 1; i < n; i++) build(read(),read());
        dfs(1);
        for (register int j = 1; j <= 18; j++)
            for (register int i = 1; i <= tot; i++){
                if (i + bin[j] - 1 > tot) break;
                mn[i][j] = dep[mn[i][j - 1]] < dep[mn[i + bin[j - 1]][j - 1]] ? mn[i][j - 1] : mn[i + bin[j - 1]][j - 1];
            }
        register int u,v,o;
        while (m--){
            u = read(); v = read(); o = lca(u,v);
            //printf("lca(%d,%d) = %d\n",u,v,o);
            //print(rt[u],1,N); puts("");
            //print(rt[v],1,N); puts("");
            pos = A[o];
            V = ask(rt[u]);
            printf("%d\n",query(rt[u],rt[v],1,N));
        }
    }
    return 0;
}

G

一眼看过去以为是裸的后缀数组,仔细一看原来串那么长
但是思想是相通的
先讲讲如果是一般的串怎么做
我们只需要写出一个\(O(1)\)\(cmp\)函数即可进行排序
我们知道比较两个字符串实质是比较第一个不同的字符【串长不够特殊考虑】
所以我们只需要\(O(1)\)求出两个位置的\(lcp\)即可
这是后缀数组拿手的地方

然而换到了这道题,我们同样可以通过求\(lcp\)来进行比较,但不是用后缀数组
我们先压缩一下,保证任意相邻两段字符不同
我们发现\(m\)很小,可以\(O(m^2)\)dp求出任意两段开头位置开始的\(lcp\),记为\(f[i][j]\)
我们对于任意两个串,对于段开头前面的部分我们可以直接判断是否相同,如果它们字符都不一样当然不同,如果字符一样就看到该段末尾的距离,由于相邻段字符是不一样的,如果距离不同,那么到段的结尾后自然就不同了,如果距离还相同,那么就可以加上\(f[i][j]\),就是\(lcp\)
综上可以\(O(nlogn)\)预处理串的位置,\(O(m^2)\)dp,\(O(nlogn)\)排序
总复杂度\(O(nlogn + m^2)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 300005,maxm = 2005,INF = 1000000000;
inline LL read(){
    LL out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
char s[maxm];
int n,m,id[maxn];
LL num[maxm],ll[maxn],len[maxn],l[maxn],lcp[maxm][maxm];
bool cmp(const int& x,const int& y){
    LL u = l[x],v = l[y],L;
    if (s[u] != s[v]) return s[u] < s[v];
    else {
        LL l1 = num[u] - ll[x] + 1,l2 = num[v] - ll[y] + 1;
        if (l1 != l2) L = min(l1,l2);
        else L = l1 + lcp[u + 1][v + 1];
    }
    L = min(L,min(len[x],len[y]));
    //printf("%d-%d  lcp = %lld\n",x,y,L);
    if (L == len[x] && L == len[y]) return x < y;
    if (L == len[x]) return true;
    if (L == len[y]) return false;
    int p1 = lower_bound(num + 1,num + 1 + m,ll[x] + L) - num;
    int p2 = lower_bound(num + 1,num + 1 + m,ll[y] + L) - num;
    return s[p1] < s[p2];
}
int main(){
    int T = read(); char c;
    LL L,R;
    while (T--){
        n = read(); m = read();
        for (int i = 1; i <= m; i++){
            c = getchar(); while (!isalpha(c)) c = getchar();
            num[i] = read();
            if (i > 1 && c == s[i - 1]){
                num[i - 1] += num[i];
                i--; m--;
            }
            else s[i] = c;
        }
        REP(i,m) lcp[i][m + 1] = lcp[m + 1][i] = 0;
        for (int i = m; i; i--)
            for (int j = m; j; j--){
                if (s[i] != s[j]) lcp[i][j] = 0;
                else if (num[i] == num[j]) lcp[i][j] = num[i] + lcp[i + 1][j + 1];
                else lcp[i][j] = min(num[i],num[j]);
                //printf("lcp(%d,%d) = %lld\n",i,j,lcp[i][j]);
            }
        for (int i = 1; i <= m; i++) num[i] += num[i - 1];
        for (int i = 1; i <= n; i++){
            L = read(); R = read(); id[i] = i;
            len[i] = R - L + 1;
            l[i] = lower_bound(num + 1,num + 1 + m,L) - num;
            ll[i] = L;
        }
        sort(id + 1,id + 1 + n,cmp);
        for (int i = 1; i <= n; i++){
            printf("%d",id[i]);
            if (i < n) putchar(' ');
        }
        if (T) puts("");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Mychael/p/9096872.html
今日推荐