A.01 Sequence

题意:对于一个长度为 333 的倍数的,元素只有01的环,你每次可以选择一个 111 删除以这个 111 为中心的相邻三个元素。你可以选择将环当中的部分 000 变成 111 ,求最少的选择数字数量使得你能够将这个环删除完毕。
给定一个长度为 nnn 的01序列,qqq 次询问,每次询问一个区间。每次询问一个区间,表示询问的环。(3≤n≤1e6,1≤q≤1e6)(3 \leq n \leq 1e6, 1 \leq q \leq 1e6)3n1e6,1q1e6

题解:题意显然可以转换成 len/3−环上选择至多的互不相邻的1的数量len/3 - 环上选择至多的互不相邻的1的数量len/3环上选择至多的互不相邻的1的数量。通过线段树维护这个值,直接区间查询即可。
注意,题目询问是个环 ,最后得到的答案应该满足,不同时选左右端点的1,且上述式子可能求出来负数,要和 000maxmaxmax
复杂度: O(n+qlogn)O(n+qlogn)O(n+qlogn)

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N =1000005;
string str;
namespace SegTree{
    #define ls rt << 1
    #define rs rt << 1 | 1
    #define lson ls, l, mid
    #define rson rs, mid + 1, r
    struct Info{
        int lmax, rmax, lrin, lrout;
        friend Info operator+ (Info a, Info b){
            return {
                max(a.lmax+max(b.lmax,b.lrout),a.lrin+b.lrout),
                max(max(a.rmax,a.lrout)+b.rmax,a.lrout+b.lrin),
                max({a.lmax+b.rmax,a.lrin+b.rmax,a.lmax+b.lrin}),
                max({a.rmax+b.lrout,a.lrout+b.lrout,a.lrout+b.lmax})
            };
        }
    }tr[N << 2];
    void push_up(int rt){ tr[rt] = tr[ls] + tr[rs]; }
    void build(int rt, int l, int r){
        if(l == r){
            if(str[l-1]=='1'){
                tr[rt]={0,0,1,0};
            }
            return;
        }
        int mid = l + r >> 1;
        build(lson), build(rson);
        push_up(rt);
    }
    Info query(int rt, int l, int r, int L, int R){
        if(l >= L && r <= R) return tr[rt];
        int mid = l + r >> 1;
        if(mid >= R) return query(lson, L, R);
        if(mid < L) return query(rson, L, R);
        return query(lson, L, R) + query(rson, L, R);
    }
}
using SegTree::build;
using SegTree::query;
inline void solve(){
    int n, q; cin >> n >> q >> str;
    build(1, 1, n);
    for(int i = 1; i <= q; i++){
        int l, r; cin >> l >> r;
        auto res = query(1, 1, n, l, r);
        cout << max((r-l+1)/3-max({res.lmax, res.rmax,res.lrout}),0ll)<< endl;
    }
}
signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    int t = 1; // cin >> t;
    while(t--) solve();
    return 0;
}

C.Delete the Tree

给一个树,你可以选择两种操作

删除:删除一个点以及所有与他相邻的边
收缩:将一个有两条边的结点与他有的边删除,并连接两个本来与他相连的结点。

求最少的删除操作删除整棵树。

题解:签到题,对于每个度数大于等于 222 的结点在删除过程中都可能使得度数等于 222 ,所以答案就是度数小于等于 111 的节点数目。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 1e6 + 10; 
int d[N],ans;
inline void solve(){
    int n; cin >> n; ans = 0;
    memset(d, 0, (n + 2) * sizeof(int));
    for(int i = 1; i < n; i++){
        int u, v; cin >> u >> v;
        d[u]++;d[v]++;
    }
    for(int i = 1; i <= n; i++){
        if(d[i] <= 1) ans++;
    }
    cout << ans << endl;
}
signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    int t = 1; cin >> t;
    while(t--) solve();
    return 0;
}

G.Read the Documentation

题意:给定一个数字 nnn 表示按照顺序有 nnn个物品,每个物品有一个权值 wiw_iwi,有费用 ttt
对于选择物品要求有

不能选择连续的五个物品
作为连续的第 iii 个物品需要花费 xix_ixi 的费用

求获得的权值最大是多少。(1≤n≤100,1≤q≤1e9)(1 \leq n \leq 100,1 \leq q \leq 1e9)(1n100,1q1e9)
题解:简单背包。
dp[i][j][k][l][m]dp[i][j][k][l][m]dp[i][j][k][l][m] 表示,前 iii 个数,jjj111 , kkk222 ,lll333 ,mmm444 的方案的最大值
满足 j∗2+k∗3+l∗4+m∗5<=i+1&&sum1∗j+sum2∗k+sum3∗l+sum4∗m≤tj*2+k*3+l*4+m*5<=i+1 \&\& sum_1*j+sum_2*k+sum_3*l+sum_4*m \leq tj2+k3+l4+m5<=i+1&&sum1j+sum2k+sum3l+sum4mt
暴力转移即可(转移式子可见代码)。
复杂度O(n5120)O(\frac{n^5}{120})O(120n5)

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,t,ans;
int x[10],a[105];
int dp[102][55][35][27][22];
void solve(){
    cin>>n>>t;
    for(int i=1;i<=4;i++){
        cin>>x[i];
        x[i]+=x[i-1];
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
        a[i]+=a[i-1];
    }
    for(int i=1;i<=n;i++){
        for(int j=0,sum1=0,res1=0;res1<=t&&sum1<=i+1&&j<=50;j++,sum1+=2,res1+=x[1]){
            for(int k=0,sum2=sum1,res2=res1;res2<=t&&sum2<=i+1&&k<=33;k++,sum2+=3,res2+=x[2]){
                for(int l=0,sum3=sum2,res3=res2;res3<=t&&sum3<=i+1&&l<=25;l++,sum3+=4,res3+=x[3]){
                    for(int m=0,sum4=sum3,res4=res3;res4<=t&&sum4<=i+1&&m<=20;m++,sum4+=5,res4+=x[4]){
                        dp[i][j][k][l][m]=dp[i-1][j][k][l][m];
                        if(j){
                            dp[i][j][k][l][m]=max(dp[i][j][k][l][m],dp[max(0ll,i-2)][j-1][k][l][m]+a[i]-a[i-1]);
                        }
                        if(k){
                            dp[i][j][k][l][m]=max(dp[i][j][k][l][m],dp[max(0ll,i-3)][j][k-1][l][m]+a[i]-a[i-2]);
                        }
                        if(l){
                            dp[i][j][k][l][m]=max(dp[i][j][k][l][m],dp[max(0ll,i-4)][j][k][l-1][m]+a[i]-a[i-3]);
                        }
                        if(m){
                            dp[i][j][k][l][m]=max(dp[i][j][k][l][m],dp[max(0ll,i-5)][j][k][l][m-1]+a[i]-a[i-4]);
                        }
                        ans=max(ans,dp[i][j][k][l][m]);
                    }
                }
            }
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	solve();
}

H.Step Debugging

题意:忘光了
题解:按照题意模拟dfs过程即可,我是被队友嘴巴操纵写的代码。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=20220911;
string s;
int dfs(){
    int res=0,w;
    string s;
    while(cin>>s){
        if(s=="for")return res;
        else if(s=="repeat"){
            int now=dfs();
            cin>>w>>s;
            (res+=now*w)%=mod;
        }
        else if(s=="library")res=(res+1)%mod;
        else if(s=="fin")return res;
    }
    return res;
}
signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    cout<<dfs()%mod;
}

J.Gachapon

题意:一个抽卡游戏,玩家想要抽中某张卡,一旦抽中立刻停止抽奖。
给定两个整数 X,YX,YXY 表示进行 XXX 轮抽卡,结束游戏的轮数期望是 YYY 。你需要构造一组包含 XXX 个数的数组 ppp 。其中 pip_ipi 表示在第 iii 轮游戏抽中卡的概率,满足 pX==1p_X==1pX==1∀1≤i<x,0<pi<1\forall{1 \leq i < x},0< p_i < 11i<x,0<pi<1 。对于所有的 pip_ipi 以最简分数形式输出,保证输出的最简分数 a/ba/ba/b 满足 a,b≤10000a,b \leq 10000a,b10000

qiq_iqi 表示进行 iii 轮游戏结束的概率,考虑将题意用公式写出则有

∑qi=1∑i∗qi=Y \begin{aligned} \sum q_i &=1 \\ \sum i*q_i&=Y \end{aligned} qiiqi=1=Y
我们需要构造序列 qqq 满足以上两个方程。假设所有 qiq_iqi 的最简分数。令分母的最小公倍数为 nnn , aia_iai 表示分母为 nnn 时候的 qiq_iqi 的分子,则有,sumisum_isumiaaa 数组前 iii 项和。对于任何一个合法的 qqq 序列,对应有 pi=ain−sumi−1p_i = \frac{a_i}{n-sum_{i-1}}pi=nsumi1ai,发现每一轮的 pip_ipi 的分母小于等于 nnn,为满足题意要求可令 n=10000n=10000n=10000,随后即有
∑ai=10000∑i∗ai=10000∗Y \begin{aligned} \sum a_i &=10000 \\ \sum i*a_i&=10000*Y \end{aligned} aiiai=10000=10000Y
考虑构造一个满足题意的解序列 aaa ,首先找到满足上式,对应期望最小值,则为 a1=10001−xa_1=10001-xa1=10001x, 其余为 111 ,然后在每次 ai−1−1,ai+1a_{i-1}-1,a_{i}+1ai11,ai+1 过程中,期望增加一,循环即可找到一个解,随后输出即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
int x,y,ex,p[1005];
void solve(){
    cin>>x>>y;
    ex=p[1]=10001-x;
    for(int i=2;i<=x;i++){
        p[i]=1;
        ex+=i;
    }
    for(;ex<y*10000;){
        for(int i=1;ex<y*10000&&i<x;i++){
            p[i]--,p[i+1]++;
            ex++;
        }
    }
    for(int i=1,fenmu=10000;i<=x;i++){
        int d=__gcd(p[i],fenmu);
        cout<<p[i]/d<<" "<<fenmu/d<<endl;
        fenmu-=p[i];
    }
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	solve();
}

K.Pyramad

题意:玩家起始攻击力为 111 ,训练可增长 111 攻击力(后续称之训练效果)。每天玩家可以选择两种操作

使训练效果+1
训练一次

每天做完选择之后会面临一个血量为 xix_ixi 的怪物,当且仅当你的攻击力大于等于怪物血量的时候才能杀死他。怪物共 nnn 个,求最多杀死多少个怪物。(1≤n,xi≤5000,)(1 \leq n,x_i \leq 5000,)(1n,xi5000,)

题解:太晚了明天写详细的。先放个代码。
读完题目感觉无从下手,先列出题目相关的变量 :怪物的数量,玩家的攻击力,训练效果,胜利的次数,而且攻击力显然至多和血量为一个数量级。因为每天可以增加攻击力或让训练效果增强很容易想到一个 O(n3)O(n^3)O(n3) 的 dp ,即令 dpi,j,kdp_{i,j,k}dpi,j,k 表示经过 iii 个怪物 ,攻击力为 jjj ,训练效果为 kkk 的最大胜利数量。这个递推可以 O(1)O(1)O(1) 转移。
考虑优化。一个较为显然的优化是训练效果至多增强到 n\sqrt nn 级别所以该维可以只用前 n\sqrt nn 部分。
简略证明:这里本来有个证明但是我一下子不知道怎么写,大致思路是这里是个凸函数。后面想明白了再补。
考虑第二个优化。容易发现当前的 dp 式子的维度无法继续优化。但是根据上一个优化结论,我们可以知道,假设我们不考虑一开始的怪物,只需要最少只需要 142142142 天即可将攻击力超过 500050005000 ,所以我们发现失败的轮数至多是 142142142
考虑将失败轮数作为 dp 的转移,令 dpi,j,kdp_{i,j,k}dpi,j,k 表示经过 iii 个怪物 ,失败次数为 jjj ,训练效果为 kkk 的最大攻击力。然后枚举每一天选择两种方案即可 O(1)O(1)O(1) 转移。由于可能存在部分无效状态所以可以使用刷表法进行转移。最后对所有有效的状态求 MAX(i−j)MAX(i-j)MAX(ij) 即可。

#include <bits/stdc++.h>
using namespace std;
int n,k,ans;
int x[5005];
int dp[5005][150][75];//前i个输j把,w到k的最大攻击力
void solve(){
    cin>>n;
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=n;i++){cin>>x[i];}
    dp[0][0][1]=1;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=142;j++){
            for(int k=1;k<=73;k++){
                if(dp[i][j][k]!=-1){
                    ans=max(ans,i-j);
                    if(dp[i][j][k]>=x[i+1]){
                        dp[i+1][j][k+1]=max(dp[i+1][j][k+1],dp[i][j][k]);
                    }
                    else{
                        dp[i+1][j+1][k+1]=max(dp[i+1][j+1][k+1],dp[i][j][k]);
                    }
                    if(dp[i][j][k]+k>=x[i+1]){
                        dp[i+1][j][k]=max(dp[i][j][k]+k,dp[i+1][j][k]);
                    }
                    else{
                        dp[i+1][j+1][k]=max(dp[i][j][k]+k,dp[i+1][j+1][k]);
                    }
                }
            }
        }
    }
    cout<<ans;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	solve();
}

L.LCS-like Problem

题意:给定两个字符串 s,ts,ts,t 求从 sss 的最长子序列满足该子序列和 ttt 的最长公共子序列小于等于 111

题解:DP,对 sss 中字母分出现在 ttt 中和没出现在 ttt 中考虑。对于没出现过的字母,贪心选择即可。对于出现在 ttt 中的字母,我们可以建立一个矩阵 cki,jck_{i,j}cki,j 表示字母 iii 后面能否添加 jjj。然后直接 26n26n26n 求最大值即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
string s,t;
int n,m;
int ck[30][30],tot[26];
int dp[30];
void solve(){
    cin>>s>>t;
    n=s.size();m=t.size();
    s=" "+s;t=" "+t;
    for(int i=m;i;i--){
        int k=t[i]-'a';
        for(int j=0;j<26;j++){
            ck[k][j]=tot[j];
        }
        tot[k]++;
    }
    int res=0,ans=0;
    for(int i=1;i<=n;i++){
        int k=s[i]-'a';
        if(tot[k]==0){
            res++;continue;
        }
        if(!ck[k][k])dp[k]=dp[k]+1;
        for(int j=0;j<26;j++){
            if(j!=k&&ck[j][k]==0){
                dp[k]=max(dp[k],dp[j]+1);
            }
        }
    }
    for(int i=0;i<26;i++){
        ans=max(ans,dp[i]);
    }
    cout<<ans+res;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	solve();
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐