【考试总结】2022-04-02

图案

先计算哪些前缀和被分成 K 个相同的字符串

设某个合法前缀是 (A)K 那么在 i+1 开始跟一段 A 的前缀都是合法的,长度可以使用 ZFunction 来计算

区间覆盖 1 我写了树状数组,没细想有没有更快的做法

upd:好像可以使用简单的 差分法 将这题做到不带对数复杂度

Code Display
const int N=1e6+10;
char s[N];
int z[N],n,k;
ull hs[N],pw[N];
bool mark[N];
inline int get(int l,int r){return hs[r]-hs[l-1]*pw[r-l+1];}
struct Fenwick_Tree{
    int c[N];
    inline void insert(int x,int v){for(;x<=n;x+=x&(-x)) c[x]+=v;}
    inline int query(int x){int res=0;for(;x;x-=x&(-x)) res+=c[x]; return res;}
}T;
signed main(){
    freopen("pattern.in","r",stdin); freopen("pattern.out","w",stdout);
    n=read(); k=read();
    scanf("%s",s+1);
    pw[0]=1;
    rep(i,1,n) pw[i]=pw[i-1]*998244353,hs[i]=hs[i-1]*998244353+s[i];
    for(int len=1;len<=n/k;++len){
        bool legal=1;
        for(int d=2;d<=k;++d){
            if(get(1,len)!=get((d-1)*len+1,d*len)){
                legal=0;
                break;
            }
        }
        mark[len*k]=legal;
    }
    z[1]=n;
    for(int i=2,l=0,r=0;i<=n;++i){
        if(i<=r) z[i]=min(z[i-l+1],r-i+1);
        while(i+z[i]<=n&&s[z[i]+1]==s[i+z[i]]) ++z[i];
        if(i+z[i]-1>r) l=i,r=i+z[i]-1;
    }
    for(int i=1;i<=n;++i) if(mark[i]){
        int mxl=min(z[i+1],i/k);
        T.insert(i,1); 
        T.insert(i+mxl+1,-1);
    }
    rep(i,1,n) putchar('0'+(bool)T.query(i));
    putchar('\n');
    return 0;
}

树点购买

前两问可以使用贪心来解决,也就是每棵子树里面剩余一个点不进行覆盖,找到根链上最小值来加到答案里面

第三问不能直接计数,考虑一类和贪心本质一样的 DP 也就是设 fx,gx 表示是/否全部覆盖的最小花费

转移分别为

gxfsonmaxfsongsonfxmin{fson,gx+ax}

方案数统计对着上面式子用简单的加法乘法即可

Code Display
const int N=1e6+10;
vector<int> rely[N],G[N];
int n,typ,a[N];
bool cho[N];
int dp[N],node[N],rem[N];
inline void dfs(int x,int fat){
    int sum=0,Mx=0;
    if(x!=1&&G[x].size()==1){
        node[x]=x;
        rem[x]=a[x];
        return ;
    }
    for(auto t:G[x]) if(t!=fat){
        dfs(t,x);
        dp[x]+=dp[t];
        if(rem[x]!=rem[t]){
            if(rem[x]<rem[t]){
                cho[node[x]]=1;
                dp[x]+=rem[x];
                node[x]=node[t]; rem[x]=rem[t];
            }else{
                cho[node[t]]=1;
                dp[x]+=rem[t];
            }
        }else{
            cho[node[x]]=cho[node[t]]=1;
            dp[x]+=rem[t];            
        }
    }
    if(rem[x]==a[x]) rely[x].emplace_back(node[x]),node[x]=x;
    else if(rem[x]>a[x]) rem[x]=a[x],node[x]=x;
    return ;
}
signed main(){
    freopen("purtree.in","r",stdin); freopen("purtree.out","w",stdout);
    n=read();
    rep(i,1,n) a[i]=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    typ=read();
    int Mn;
    if(typ>=1){
        dfs(1,0);
        print(Mn=dp[1]+rem[1]);
        putchar('\n');
    }
    if(typ>=2){
        cho[node[1]]=1;
        queue<int> q;
        rep(i,1,n) if(cho[i]) q.push(i);
        while(q.size()){
            int fr=q.front(); q.pop();   
            for(auto t:rely[fr]) if(!cho[t]){
                cho[t]=1; 
                q.push(t);
            }
        }
        rep(i,1,n) if(cho[i]) print(i); 
        putchar('\n');
    }
    if(typ>=3){
        vector<int> f(n+1),g(n+1);
        vector<int> mf(n+1),mg(n+1);
        function<void(int,int)>DFS=[&](const int x,const int fat){
            if(x!=1&&G[x].size()==1){
                f[x]=a[x];
                mf[x]=mg[x]=1;
                return ;
            }
            vector<int> son;
            int Mx=-1e18;
            int Multf=1,sigf=0;
            for(auto t:G[x]) if(t!=fat){
                DFS(t,x);
                if(Mx<f[t]-g[t]){
                    Mx=f[t]-g[t];
                    son={t};
                }else if(Mx==f[t]-g[t]){
                    son.emplace_back(t);
                }
                ckmul(Multf,mf[t]);
                sigf+=f[t];
            }
            g[x]=sigf-Mx;
            for(auto t:son) ckadd(mg[x],mul(mul(mg[t],ksm(mf[t],mod-2)),Multf));
            if(sigf==g[x]+a[x]){
                mf[x]=add(mg[x],Multf);
                f[x]=g[x]+a[x];
            }else if(sigf<g[x]+a[x]){
                mf[x]=Multf;
                f[x]=sigf;
            }else{
                mf[x]=mg[x];
                f[x]=g[x]+a[x];
            }
            return ;
        };
        DFS(1,0);
        print(mf[1]);
        putchar('\n');
    }
    return 0;
}

舰队游戏

fi,j 表示 i 点还有 jHP 想要走到终点的期望步数

转移分成走到后继节点和返回 1 号点补血两种:

fi,j=min{1degifv,jdv,f1,H+Hj}

此时所有的目光都落到了 f1,H 上面,那么可以发现所有的 fi,j 可以表达成 kf1,H+b 的形式

尝试给 f1,H 假定权值 x 并拓扑排序得到 f1,H 和自己的表达关系,显然要满足 k=1,b=0

根据实际含义可以发现如果 f1,H 小于实际值时会趋向于选择第二个决策,而大于实际值时趋向于第二个决策,所以二分答案找到最后一个满足斜率为 1 的地方即可

Code Display
const int N=110;
vector<int> G[N];
int n,m,d[N],H;
pair<double,double> dp[N][N];
inline double calc(double V){
    for(int i=n-1;i>=1;--i){
        int Mx=0;
        for(auto t:G[i]) ckmax(Mx,d[t]);
        rep(j,1,H){
            dp[i][j]={0,0};
            if(j<=Mx||!G[i].size()) dp[i][j]={V+1.0*H-j,1.0};
            else{
                for(auto t:G[i]){
                    dp[i][j].fir+=dp[t][j-d[t]].fir;        
                    dp[i][j].sec+=dp[t][j-d[t]].sec;
                }
                dp[i][j].fir/=1.0*G[i].size();
                dp[i][j].sec/=1.0*G[i].size();
                dp[i][j].fir++;
                if(dp[i][j].fir>V+H-j){
                    dp[i][j].sec=1;
                    dp[i][j].fir=V+H-j;
                }
            }
        }
    }
    return dp[1][H].sec;
}
signed main(){
    freopen("kancolle.in","r",stdin); freopen("kancolle.out","w",stdout);
    n=read(); m=read(); H=read();
    for(int i=1;i<=m;++i){
        int u=read(),v=read();
        G[u].emplace_back(v);
    }
    rep(i,1,n) d[i]=read();
    double l=0,r=1e6,ans=r+1;
    if(calc(ans)==1) puts("-1"),exit(0);
    while(r-l>1e-9){
        double mid=(l+r)/2;
        if(calc(mid)<1) r=mid;
        else l=mid,ans=mid;
    }
    printf("%.10lf\n",ans);
    return 0;
}

posted @   没学完四大礼包不改名  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示