牛客2021年七夕节比赛大部分题解(因数枚举模板、线段树、dp……)

传送门

偶然刷到了,就做了一下。F是防AK,不会,ACDE水,略。

比较有参考价值的是I题,dp。

B

一开始想的是状压枚举素数集合,但是各素因子的幂不一定要统一。于是改成了直接dfs枚举所有素数的所有幂次,并逐一判定。

写完了一发AC了以后才发现,枚举所有素数的所有幂次也就是枚举所有因数,23333我是sb。不过这个枚举方法肯定比根号枚举要快一点。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 32000 + 5;

int n;bool vis[N];vector<int> primes;

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read(){}
template<typename T,typename ...R>void read(T &x,R &...r){
    read(x);
    read(r...);
}

void init_p(int m){
    vis[1] = true;
    rep(i,2,m){
        if(!vis[i]) primes.push_back(i);
        for(int p: primes){
            if(i > m / p) break;
            vis[i * p] = true;
            if(i % p == 0) break;
        }
    }
}

void dfs(vector<vector<int> > &vec,int &ans,int dep = 0,int mul = 1){
    if(dep >= vec.size()){
        if(mul == 1) return;
        int x = n;while(x % mul == 0) x /= mul;
        if(x < mul) ans = min(ans,mul);
        return;
    }
    int p = vec[dep][0];
    for(int cur = 1;;cur *= p){
        dfs(vec,ans,dep+1,mul*cur);
        if(cur == vec[dep][1]) break;
    }
}

int solve(){
    int x = n;
    vector<vector<int> > vec;
    for(int p: primes){
        if(p > x/p) break;
        int pw = 1;
        while(x % p == 0){
            x /= p;
            pw *= p;
        }
        if(pw > 1) vec.push_back({p,pw});
    }
    if(x > 1) vec.push_back({x,x});
    int ans = n;
    dfs(vec,ans);
    return ans;
}

int main(int argc, char** argv) {
    init_p(N-5);
    int T;read(T);
    while(T--){
        read(n);
        printf("%d\n",solve());
    }
    return 0;
}
G

或运算有如下单调性:两个集合是子集关系,则子集的or值小于等于超集的or值。所以这题相当于询问区间或,以及区间赋值。套线段树模板。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
#define ls(x) ((x) << 1)
#define rs(x) ((x) << 1 | 1)

const int N = 1e6 + 5;

int n,m,a[N];

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read(){}
template<typename T,typename ...R>void read(T &x,R &...r){
    read(x);
    read(r...);
}

struct Node{int sum,tg;}nd[N<<2];

void pushup(int x){nd[x].sum = nd[ls(x)].sum | nd[rs(x)].sum;}
void pushdown(int x){
    int tg = nd[x].tg;
    if(!tg) return;
    nd[ls(x)].tg = nd[rs(x)].tg = nd[ls(x)].sum = nd[rs(x)].sum = tg;
    nd[x].tg = 0;
}

void build(int l,int r,int x){
    if(l == r){
        nd[x] = {a[l],0};return;
    }
    int mid = (l+r)>>1;
    build(l,mid,ls(x));
    build(mid+1,r,rs(x));
    pushup(x);
}

void mdy(int ql,int qr,int v,int l = 1,int r = n,int x = 1){
    if(ql <= l && r <= qr){
        nd[x] = {v,v};return;
    }
    pushdown(x);
    int mid = (l+r)>>1;
    if(ql <= mid) mdy(ql,qr,v,l,mid,ls(x));
    if(qr > mid) mdy(ql,qr,v,mid+1,r,rs(x));
    pushup(x);
}

int qry(int ql,int qr,int l = 1,int r = n,int x = 1){
    if(ql <= l && r <= qr) return nd[x].sum;
    pushdown(x);
    int mid = (l+r)>>1,ans = 0;
    if(ql <= mid) ans |= qry(ql,qr,l,mid,ls(x));
    if(qr > mid) ans |= qry(ql,qr,mid+1,r,rs(x));
    return ans;
}

int main(int argc, char** argv) {
    read(n,m);
    rep(i,1,n) read(a[i]);
    build(1,n,1);
    while(m--){
        int op;read(op);
        if(op == 1){
            int l,r;read(l,r);
            printf("%d\n",qry(l,r));
        }
        else{
            int l,r,x;read(l,r,x);
            mdy(l,r,x);
        }
    }
    return 0;
}
H

这题给定的线段,其实是一个特殊的有向图。之前某场abc考了一个有向图最短路的问题,就是在边权的基础上加了一个特性:某个节点a秒开启b秒关闭,无限交替。这个问题的关键性质就是:立刻走不会比”等一会再走“更差

利用这个性质随便模拟一下就可以过了。判定是否要等待:

int tmp = ans % (pt[i].a + pt[i].b);
if(tmp < pt[i].a) ans += pt[i].a - tmp;

注意:别忘了先对位置升序排序。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 1e5 + 5;

int n,m;

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read(){}
template<typename T,typename ...R>void read(T &x,R &...r){
    read(x);
    read(r...);
}

struct Node{int p,a,b;}pt[N];
bool cmp(Node &x,Node &y){return x.p < y.p;}

int main(int argc, char** argv) {
    read(n,m);
    rep(i,1,m) read(pt[i].p,pt[i].a,pt[i].b);
    sort(pt+1,pt+m+1,cmp);
    LL ans = 0;
    rep(i,1,m){
        ans += pt[i].p-pt[i-1].p;
        int tmp = ans % (pt[i].a + pt[i].b);
        if(tmp < pt[i].a) ans += pt[i].a - tmp;
    }
    ans += n-pt[m].p;
    printf("%lld\n",ans);
    return 0;
}
I

题干的描述,就是01交替,别被蒙蔽了。看到子串+计数,第一个想法就是”前缀定义dp“。这里我们选择了定死交替的起点(定死终点也行),所以定义dp[i][0]为从i开始且s[i]固定为'F'最大长度dp[i][1]同理。

题目要求”偶数“长度,但上面定义的是所有合法长度,且认定长度为1的交替串也合法,因为后来发现偶数长度可以由任意长度得到。

转移:非法(s[i]的值和你要定死的值不符)则dp[i][u] = 0。合法则dp[i+1][!u]+1。注意dp[i+1][!u] = 0的时候这个转移式也是对的。

求答案:”可能为“这个提问,实际上是要求我们选择一个最优的填法,使得从当前点开始的交替串最长。并且显然合法方案是聚集在一个区间里的,所以取floor(max(dp[i][0],dp[i][1])/2)为当前点对答案的贡献。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 2e5 + 5;

int n,dp[N][2];char s[N];

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read(){}
template<typename T,typename ...R>void read(T &x,R &...r){
    read(x);
    read(r...);
}

int main(int argc, char** argv) {
    scanf("%s",s+1);
    n = strlen(s+1);
    dp[n][0] = s[n] != 'W';
    dp[n][1] = s[n] != 'F';
    dwn(i,n-1,1){
        rep(u,0,1){
            if((!u && s[i] == 'W') || (u && s[i] == 'F')) continue;
            int v = dp[i+1][!u];
            dp[i][u] = v+1;
        }
    }
    // rep(i,1,n) dbg(dp[i][0],dp[i][1]);//
    LL ans = 0;
    rep(i,1,n) ans += max(dp[i][0],dp[i][1])/2;
    printf("%lld\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值