CCPC+ICPC预备

CCPC 网络赛 Remove

每个数,用线性筛+递推求他的最大质因子。 O ( n ) O(n) O(n)
单调队列优化转移:

f[j]=min(f[j],f[i]) i+1<=j<=i+mxp[i]-1
//
// Created by artist on 2021/9/2.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define ull unsigned long long
#define pii pair<int,int>

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

const int maxn = 2e6+6;
const int maxp = 1e5+5;
int pri[maxp];
int mnp[maxn],mxp[maxn]; // 每个数的最小质因数,每个数的最大质因数
int cnt,pr[maxn],vis[maxn],exi[maxn];
deque<pii> q;
ull base[maxn],ans[maxn];
ull bs = 23333;
void init(){
    base[0]=1;
    base[1]=23333;
    for(int i=2;i<maxn;++i){
        base[i]=base[i-1]*bs;
        if(!vis[i]) pr[++cnt]=i,mnp[i]=i;
        for(int j=1;j<=cnt&&1ll*pr[j]*i<maxn;++j){
            vis[i*pr[j]]=1;
            mnp[i*pr[j]]=pr[j];
            if(i%pr[j]==0) break;
        }
    }
}

signed main() {
    ios::sync_with_stdio(0);cin.tie(0);
    int t;cin>>t;
    init();
    while(t--){
        int n,p;cin>>n>>p;
        int mxpp=0;
        for(int i=1;i<=p;++i){
            cin>>pri[i];
            exi[pri[i]]=1;
            mxpp=max(mxpp,pri[i]);
        }

        q.clear();
        q.push_back(mkp(1,mxpp-1));
        for(int i=1;i<=n;++i){
            if(i==1) mxp[i]=-1;
            else mxp[i]=mxp[i/mnp[i]];
            if(exi[mnp[i]]) mxp[i]=max(mxp[i],mnp[i]);
            ans[i]=0;
            while(i>q.front().se && q.size()) q.pop_front();
            if(!q.empty()) ans[i]=q.front().fi;
            if(ans[i] && mxp[i]!=-1) q.push_back(mkp(ans[i]+1,mxp[i]-1+i));
        }

        ull res=0;
        for(int i=1;i<=n;++i){
            res = res + base[n-i]*(ull)ans[i];
        }
        cout<<res<<endl;
        for(int i=1;i<=p;++i) exi[pri[i]]=0;
    }
}
/*
 * 1
20 4
2 3 5 7
 */

“红旗杯”第十五届东北地区大学生程序设计竞赛 Vertex Deletion

树形dp。
dp1:删除点u,子树中的合法方案数。
dp2:保留点u与点u的儿子,~方案数。
dp3:仅仅保留点u,点u的儿子均被删除,~方案数(此时因为点u,这个情况不合法,最后不会计入答案)

//
// Created by Artist on 2021/9/4.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

const int maxn = 1e5+5;
const int mod = 998244353;
vector<int> G[maxn];
// dp1:删除点u,子树中的合法方案数。
// dp2:保留点u与点u的儿子,~方案数。
// dp3:仅仅保留点u,点u的儿子均被删除,~方案数(此时因为点u,这个情况不合法,最后不会计入答案)
ll dp1[maxn],dp2[maxn],dp3[maxn];
void dfs(int u,int fa){
    dp1[u]=dp2[u]=dp3[u]=1;
    for(auto v:G[u]){
        if(v==fa) continue;
        dfs(v,u);
        dp1[u]=(dp1[v]+dp2[v])%mod*dp1[u]%mod;
        dp2[u]=(dp1[v]+dp2[v]+dp3[v])%mod*dp2[u]%mod;
        dp3[u]=dp3[u]*dp1[v]%mod;
    }
    dp2[u]=(dp2[u]-dp3[u]+mod)%mod;
}

signed main() {
    ios::sync_with_stdio(0);cin.tie(0);
    int t;cin>>t;
    while(t--){
        int n;cin>>n;
        for(int i=1;i<=n;++i) G[i].clear();
        for(int i=1,u,v;i<n;++i){
            cin>>u>>v;
            G[u].pb(v);
            G[v].pb(u);
        }
        dfs(1,-1);
        cout<<(dp1[1]+dp2[1])%mod<<endl;
    }
}

I. Yet Another String Matching Problem

fft的典型应用

//
// Created by artist on 2021/9/5.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}
const int maxn = 125010;
string s,t;

const double PI = acos(-1.0);
struct Complex {
    double x, y;
    Complex(double _x = 0.0, double _y = 0.0) {
        x = _x;
        y = _y;
    }
    Complex operator-(const Complex &b) const {
        return Complex(x - b.x, y - b.y);
    }
    Complex operator+(const Complex &b) const {
        return Complex(x + b.x, y + b.y);
    }
    Complex operator*(const Complex &b) const {
        return Complex(x * b.x - y * b.y,x * b.y + y * b.x);
    }
};

int rev[maxn<<2];
// O(n) 位逆序置换
void change(Complex y[], int len) {
    for (int i = 0; i < len; ++i) {
        rev[i] = rev[i>>1] >> 1;
        if (i & 1) rev[i] |= len >> 1;
    }
    for (int i = 0; i < len; ++i)
        if (i < rev[i]) swap(y[i], y[rev[i]]);
}

// 非递归fft
void fft(Complex y[], int len, int on) {
    change(y, len);
    for (int h = 2; h <= len; h <<= 1) {
        Complex wn(cos(2 * PI / h), sin(on * 2 * PI / h));
        for (int j = 0; j < len; j += h) {
            Complex w(1, 0);
            for (int k = j; k < j + h / 2; ++k) {
                Complex u = y[k];
                Complex t = w * y[k + h / 2];
                y[k] = u + t;
                y[k + h / 2] = u - t;
                w = w * wn;
            }
        }
    }
    if (on == -1)
        for (int i = 0; i < len; ++i) y[i].x /= len;
}

Complex f[maxn<<2], g[maxn<<2];
bitset<maxn<<2> bs[6][6];
int fa[8];

int find(int x){
    return fa[x]=fa[x]==x?x:find(fa[x]);
}

signed main() {
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>s>>t;
    int slen = s.size(),tlen = t.size();
    int len = 1;
    while(len<slen*2||len<tlen*2) len<<=1;
    for(int i=0;i<6;++i){
        for(int j=0;j<6;++j){
            for(int k=0;k<slen;++k)
                f[k]=Complex((s[k]==('a'+i)?1:0),0);
            for(int k=slen;k<len;++k) f[k]=Complex(0,0);
            for(int k=0;k<tlen;++k)
                g[tlen-1-k]=Complex((t[k]==('a'+j)?1:0),0);
            for(int k=tlen;k<len;++k) g[k]=Complex(0,0);
            fft(f,len,1);
            fft(g,len,1);
            for(int k=0;k<len;++k) f[k]=f[k]*g[k];
            fft(f,len,-1);
            for(int k=0;k<len;++k) if((int)(f[k].x+0.5)) bs[i][j].set(k);
        }
    }
    // fft
    for(int i=tlen-1;i<slen;++i){
        for(int j=0;j<6;++j) fa[j]=j;
        for(int j=0;j<6;++j){
            for(int k=0;k<6;++k){
               // DB1(j,k,i-tlen,int(has[j][k][i].x+0.5));
                if(bs[j][k][i]) {
                    int fj=find(j),fk=find(k);
                    if(fj!=fk) fa[fj]=fk;
                }
            }
        }

        int comcnt = 0;
        // 边数 = 点数 - 联通分量数
        for(int j=0;j<6;++j) if(fa[j]==j) comcnt++;
        cout<<6-comcnt<<" ";
    }
}

NWERC 2018 J. Jinxed Betting

学习的zqy1018的处理方式。
(推导后)题意:给出一个长度为n的序列,一个正整数target,有一个操作:假设序列最大值为max,a[i]==max的值有k个,那么 ⌊ k 2 ⌋ \lfloor \frac{k}{2} \rfloor 2k个a[i]==max的值+1,且a[i]!=max的值+1。问多少次操作,使得数达到target+1。输出这个操作数-1作为答案。
设次大值为sec,次大值的个数为l。
手玩后发现一个性质:进行 ⌊ log ⁡ 2 k ⌋ + 1 \lfloor\log_2k \rfloor+1 log2k+1次操作后,最大值和次大值的差距减小1:最大值增加了 ⌊ log ⁡ 2 k ⌋ \lfloor\log_2k \rfloor log2k,次大值增加了 ⌊ log ⁡ 2 k ⌋ + 1 \lfloor\log_2k \rfloor+1 log2k+1
那么考虑进行 k − l k-l kl ⌊ log ⁡ 2 k ⌋ + 1 \lfloor\log_2k \rfloor+1 log2k+1次操作。此时,最大值的个数变为 k + l k+l k+l。(即,原先的次大值此时融入了最大值)。且这么多次操作中,最大值增加了 ( k − l ) × ⌊ log ⁡ 2 k ⌋ (k-l)\times \lfloor\log_2k \rfloor (kl)×log2k
那么我们就能通过这样压缩操作,分块到达target。模拟即可。
但是模拟细节比较多,不太会写,就参考了代码。。
首先用一个栈维护当前待合并的不同值以及每个值的个数。

//
// Created by Artist on 2021/9/11.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}
const int maxn = 1e5+5;

void io(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);}

ll targ,a[maxn];
stack<pair<ll,int> > st;
int logg[maxn];

signed main() {
    io();
    int n;cin>>n;n--;
    cin>>targ;
    for(int i=1;i<=n;++i) cin>>a[i];
    sort(a+1,a+1+n);

    for(int i=1,j;i<=n;i=j){
        j=i;
        while(j<=n && a[i] == a[j]) ++j;
        st.push(mkp(a[i],j-i));// 某值,该值对应的数量。在栈中从小到大排列
    }

    // 预处理Log(这个算出来是\floor(x)+1)
    logg[1] = 1;
    for(int i=2;i<=n;++i) logg[i]=logg[i>>1]+1;

    ll tot_round = 0;// 进行的轮数(为块大小)
    while(st.size()>1){
        ll ori_x1 = st.top().fi + tot_round,x1 = ori_x1;
        int a1 = st.top().se;
        st.pop();
        ll x2 = st.top().fi + tot_round;
        int a2 = st.top().se;
        st.pop();
        // ori_x1: 当前最大值;x1:原值
        // ori_x2: 当前次大值;x2:原值

        ll round_needed = (x1-x2)*logg[a1]; // 跨越的轮数
        ll x1_added = round_needed - (x1 - x2); // x1增加的轮数
        x1 += x1_added;
        if(x1>targ){ // 如果最大值大于目标值,退一下,输出
            // 怎么退??
            // 需要进行多少次增加轮*每次增加轮要的总轮数
            ll round_before = ((targ-ori_x1)/(logg[a1]-1))*logg[a1];
            // 这么多次增加轮对max造成的影响.(下取整)
            ll ori_x1_added = ((targ-ori_x1)/(logg[a1]-1))*(logg[a1]-1);
            // 余下的直接一轮max+1,增加到targ即可
            cout<<tot_round+round_before+(targ-ori_x1-ori_x1_added)<<endl;
            return 0;
        }else{
            // 否则,继续冲
            tot_round += round_needed;
            x1 -= tot_round;// 使得统一,(反正接下来就把tot_round加回来了)
            st.push(mkp(x1,a1+a2));
        }
    }

    // 到这里,就说明,全部人都同分了,还没到targ.只需要计算剩下所需多少轮即可(同上面输出方式)
    ll x1 = st.top().fi + tot_round;
    ll round_before = ((targ-x1)/(logg[n]-1))*logg[n];
    ll x1_added = ((targ-x1)/(logg[n]-1))*(logg[n]-1);
    cout<<tot_round+round_before+(targ-x1-x1_added)<<endl;
}

Edu45 G. GCD Counting

点分治的思想+考虑到一条链的2e5内的gcd个数较少,以这个为突破口暴力。

//
// Created by artist on 2021/9/12.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
#define pii pair<int,int>

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }

const int maxn = 2e5+5;

int a[maxn];
ll ans[maxn];
vector<int> G[maxn];

map<int,ll> cnt[maxn];
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}

// 点分治思想
void dfs(int u,int fa){
    cnt[u][a[u]]++; // 以u为起点,子树链为a[u]的个数
    for(auto v:G[u]){
        if(v==fa) continue;
        dfs(v,u);
        // 点分治
        for(auto x:cnt[u]){
            for(auto y:cnt[v]){
                ans[gcd(x.fi,y.fi)] += x.se*y.se;
            }
        }
        for(auto y:cnt[v]){
            cnt[u][gcd(a[u],y.fi)] += y.se;
        }
        cnt[v].clear();
    }
}

signed main() {
    io();int n;cin>>n;
    for(int i=1;i<=n;++i) cin>>a[i],ans[a[i]]++;
    for(int i=1;i<n;++i) {
        int x,y;cin>>x>>y;
        G[x].pb(y);G[y].pb(x);
    }
    dfs(1,0);
    for(int i=1;i<=2e5;++i){
        if(ans[i]) cout<<i<<" "<<ans[i]<<endl;
    }
}

牛客挑战赛52 天平

dp+递推gcd

//
// Created by artist on 2021/9/17.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
#define pii pair<int,int>

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 55;
ll dp[55][55*55]; // 到几号点,权值和为__的时候,最小的sum
vector<int> G[maxn];
ll ggcd[maxn*maxn][maxn*maxn];
ll minn[maxn*maxn];

int a[maxn];
int m;

void dfs(int u) {
    if(!G[u].size()) {
        for(int i=1;i<=m;++i) {
            dp[u][a[i]]=0;
        }
        return;
    }
    int ls = G[u][0],rs = G[u][1];
    dfs(ls);dfs(rs);
    for(int i=0;i<maxn*maxn;++i) minn[i]=1e16;
    // 枚举子树权值大小
    for(int i=0;i<maxn*maxn;++i) {
        for(int j=0;j<maxn*maxn;++j) {
            if(i+j>=maxn*maxn) break;
            minn[i+j]=min(minn[i+j],dp[ls][i]+dp[rs][j]+ggcd[i][j]);
           // DB1(minn[i+j]);
        }
    }
    // 要有i+j和对应的值成对...
    for(int i=1;i<=m;++i) {
        for(int j=0;j<maxn*maxn;++j) {
            if(a[i]+j>=maxn*maxn) continue;
            dp[u][a[i]+j] = min(dp[u][a[i]+j],minn[j]);
        }
    }
}

signed main() {
    int n;cin>>n>>m;
    for(int i=0;i<maxn;++i){
        for(int j=0;j<maxn*maxn;++j) {
            dp[i][j]=1e16;
        }
    }
    for(int i=1;i<=m;++i) {
        cin>>a[i];
    }
    for(int i=0; i<maxn*maxn; ++i) ggcd[i][0]=ggcd[0][i]=i;
    for(int i=1;i<maxn*maxn;++i){
        for(int j=1;j<maxn*maxn;++j) {
            if(j>=i) ggcd[i][j] = ggcd[i][j-i];
            else ggcd[i][j]=ggcd[j][i];
        }
    }
    for(int i=1;i<maxn*maxn;++i) {
        for (int j = 1; j < maxn * maxn; ++j) {
            ggcd[i][j] = (i + j) / ggcd[i][j];
        }
    }
    for(int i=1;i<=((n-1)>>1);++i) {
        int x,y,z;cin>>x>>y>>z;
        G[x].pb(y);
        G[x].pb(z);
    }
    dfs(1);
    ll mn = 1e16;
    for(int i=0;i<maxn*maxn;++i) {
       mn = min(mn,dp[1][i]);
    }
    cout<<mn<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值