• Gym102956 部分题解


    Contest Link

    带更。

    感谢 @hht2005 的帮助。

    B. Beautiful Sequence Unraveling

    定义一个长度为 (n) 好序列 (a) 是不存在一个位置 (1le i<n) 使得 (max{a_1,ldots,a_i}=min{a_{i+1},ldots,a_n}) 的序列。
    对长度为 (n),值域为 ([1,k]) 的好序列计数,对 (p) 取模并输出。
    (1le nle 400)(1le kle 10^8)(998244353le ple 10^9+9)(pin ext{prime})

    Solution

    (k) 是坑你的,如果考虑和 (k) 有关的直接凉凉。
    考虑回避 (k),不难发现合法的序列出现的不同的数字只有 (n) 种,直接离散化。
    考虑 (f_{i,j}) 表示一个长度为 (i) 的序列,值域为 ([1,j]) 的好序列个数。
    好序列并不好求,但考虑一个不好序列 (a),设其最大的位置 (p) 使得 (max{a_1,ldots,a_p}=min{a_{p+1},ldots,a_n}),容易发现一个大性质:(a_{p+1},ldots,a_n) 是一个好序列,如果其不是一个好序列的话,(p) 便不是最大的。
    枚举最大的位置 (p),枚举其值 (m) 可以得到 DP 式子:

    [f_{i,j}=j^i-sum^{i-1}_{p=1}sum^j_{m=1}(m^p-(m-1)^p)(f_{i-p,j-m+1}-f_{i-p,j-m}) ]

    使用前缀和优化可以做到 (O(n^3))
    考虑再设 (g_i) 表示长度为 (n) 的序列,值域为 ([1,i]) 的好序列个数:

    [g_i=f_{n,i}-sum^{i-1}_{j=1}inom i j g_j ]

    所以答案为:(displaystyle sum^n_{i=1}inom k i g_i)
    组合数暴力计算,时间复杂度 (O(n^3))

    Code
    const int N=400;
    int n,K,mod,f[N+10][N+10],ans,g[N+10],sum[N+10][N+10][N+10];
    void Add(int &x,int y) {
        x+=y;
        if(x>=mod) x-=mod;
    }
    void Del(int &x,int y) {
        x-=y;
        if(x<0) x+=mod;
    }
    int fpow(int x,int y) {
        int ret=1;
        for(;y;y>>=1) {
            if(y&1) ret=1ll*ret*x%mod;
            x=1ll*x*x%mod;
        }
        return ret;
    }
    int C(int n,int m) {
        int ret=1,fm=1;
        for(int i=n;i>=n-m+1;i--) ret=1ll*ret*i%mod;
        for(int i=1;i<=m;i++) fm=1ll*fm*i%mod;
        return 1ll*ret*fpow(fm,mod-2)%mod;
    }
    //n!/(n-m)!/m!
    int po[N+10][N+10],inv[N+10][N+10];
    void init() {
        for(int i=1;i<=n;i++) {
            po[i][0]=inv[i][0]=1;
            inv[i][1]=fpow(po[i][1]=i,mod-2);
            for(int j=2;j<=n;j++) po[i][j]=1ll*po[i][j-1]*i%mod,inv[i][j]=1ll*inv[i][j-1]*inv[i][1]%mod;
        }
    }
    int main() {
        n=read(),K=read(),mod=read();
        init();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) {
                f[i][j]=po[j][i];
                for(int t=1;t<=j;t++) {
                    int A=po[t][i],B=po[t-1][i];
                    int sum1=(sum[i-1][j-t][t]-sum[i-1][j-t+1][t]+mod)%mod;
                    int sum2=(sum[i-1][j-t+1][t-1]-sum[i-1][j-t][t-1]+mod)%mod;
                    Add(f[i][j],(1ll*A*sum1%mod+1ll*B*sum2%mod)%mod);
                }
                for(int t=1;t<=n;t++) sum[i][j][t]=(sum[i-1][j][t]+1ll*f[i][j]*inv[t][i]%mod)%mod;
            }
        for(int i=1;i<=n;i++) {
            g[i]=f[n][i];
            for(int j=1;j<i;j++) Del(g[i],1ll*C(i,j)*g[j]%mod);
        }
        for(int i=1;i<=n;i++) Add(ans,1ll*C(K,i)*g[i]%mod);
        write((ans+mod)%mod),enter;
        return WDNMD;
    }
    

    C. Brave Seekers of Unicorns

    定义一个好序列满足如下条件:

    • 序列非空。
    • 没有三个连续的元素异或和 (=0)
    • 序列递增。
    • 序列的值域为 ([1,n])

    给定 (n),对好序列计数,答案对 (998244353) 取模。

    Solution

    (f_i) 表示以 (i) 结尾的所有方案数,有:

    [f_i=sum^{i-1}_{j=1}f_j-[joplus i<j]f_{joplus i} ]

    (f_j) 可以一波前缀和带走,考虑后面,可以变形为 (sum_{k<koplus i<i} f_k)
    因为要考虑 (ioplus joplus k=0) 的情况,所以一个位上最多只有两个 (1),枚举二进制上 (i) 的除最高位的 (1)(d),钦定 (j) 的第 (d) 位是 (1)(k) 的第 (d) 位为 (0),那么第 (d) 位往后都可以随便乱取。
    那么 (k) 的范围就是 ([2^d,2^{d+1}-1]),也可以前缀和了。
    时间复杂度 (O(nlog n))

    Code
    const int N=1e6,mod=998244353;
    ll f[N+10],n,sum[N+10];
    int main() {
        scanf("%d",&n);
        f[1]=1;sum[1]=1;
        for(int i=2;i<=n;i++) {
            f[i]=sum[i-1]+1;
            int j;
            for(j=20;j>=0;j--)
                if(i&(1<<j)) break;
            j--;
            for(;j>=0;j--) if(i&(1<<j)) f[i]=(f[i]-sum[(1<<(j+1))-1]+sum[(1<<j)-1]+mod)%mod;
            sum[i]=(f[i]+sum[i-1])%mod;
        }
        printf("%lld
    ",sum[n]%mod);
    }
    

    D. Bank Security Unification

    从一个长度为 (n) 序列 (a) 中取出一个子序列,使得相邻两个元素的按位与值的和最大。

    (2le nle 10^6)(0le a_ile 10^{12})

    Solution

    我们希望按位与和最大,就是在希望相邻两个元素的二进制位数尽可能相同。
    考虑 (f_i) 表示末位为 (i) 的子序列答案最大是多少,朴素转移是 (O(n^2)) 的。
    考虑剪枝,如果考虑当前的一位,我们想从哪里转移而来,显然是与之相同的位,如果选这些位靠后一定较靠前的优秀。
    所以记录 (las_{i,0/1}) 表示第 (i) 位上一个是 (0/1) 在哪里,每次只从对应的 (las) 转移而来。
    时间复杂度 (O(nlog a_i))

    Code
    const int N=1e6;
    int n;
    ll f[N+10],g[N+10],ans;
    int las[44][2];
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&f[i]);
        g[2]=(f[1]&f[2]);
        for(int i=1;i<=2;i++)
            for(int j=39;j>=0;j--)
                if(f[i]&(1ll<<j)) las[j][1]=i;
                else las[j][0]=i;
        for(int i=3;i<=n;i++) {
            for(int j=39;j>=0;j--) {
                int zz=(bool)(f[i]&(1ll<<j));
                g[i]=max(g[i],g[las[j][zz]]+(f[las[j][zz]]&f[i]));
            }
            for(int j=39;j>=0;j--)
                if(f[i]&(1ll<<j)) las[j][1]=i;
                else las[j][0]=i;
        }
        for(int i=1;i<=n;i++) ans=max(ans,g[i]);
        printf("%lld
    ",ans);
    }
    

    E. Brief Statements Union

    给定 (n) 个数字,(k) 条限制,限制形如 (a_lAnd a_{l+1}AndldotsAnd a_{r}=x)

    对于某一条限制,如果可以删去这条限制,使得存在满足条件的序列,称这是好的。

    输出哪些序列是好的,哪些序列是坏的。

    (1le n,kle 10^6)(0le x_ile 10^{18})

    Solution

    按位考虑,那么剩下的限制分为两类:(1) 限制和 (0) 限制。
    覆盖掉 (1) 限制,考虑 (0) 限制的冲突个数:

    • (0):全都可以。
    • (1):那个冲突的是内鬼,刀了。
    • (>1):咋整都不行。

    (0) 限制的情况就考虑完了。
    考虑如何删除 (1) 限制,对于每一个只被 (1) 限制覆盖一次的点,我们可以删除这个 (1) 限制,来满足若干 (0) 限制。
    对于某一个 (0) 限制,我们将能够删除的 (1) 限制,使得该 (0) 限制合法化的 (1) 限制取出,对所有 (0) 限制的如此集合取交。
    运用差分可以做到 (O(nlog x_i))

    Code
    const int N=1e6;
    int n,K;
    int ql[N+10],qr[N+10];
    ll qx[N+10],f[N+10],g[N+10];
    int nxt[N+10],pre[N+10],ans[N+10];
    int vio[N+10],tot,id,pos[N+10],seg[N+10];
    int main() {
        scanf("%d %d",&n,&K);
        for(int i=1;i<=K;i++) scanf("%d %d %lld",&ql[i],&qr[i],&qx[i]);
        nxt[n+1]=n+1,pre[0]=0;
        for(int k=0;k<60;k++) {
            for(int i=1;i<=n;i++) f[i]=g[i]=0;
            for(int i=1;i<=K;i++)
                if((1ll<<k)&qx[i])
                    f[ql[i]]++,f[qr[i]+1]--,
                    g[ql[i]]+=i,g[qr[i]+1]-=i;
            for(int i=1;i<=n;i++) f[i]+=f[i-1],g[i]+=g[i-1];
            for(int i=n;i>=1;i--) nxt[i]=f[i]?nxt[i+1]:i;
            id=tot=0;
            for(int i=1;i<=K;i++)
                if(!((1ll<<k)&qx[i]))
                    if(nxt[ql[i]]>qr[i])
                        vio[++tot]=i;
            if(tot==0) {
                for(int i=1;i<=K;i++) ans[i]++;
                continue;
            }
            if(tot==1) ans[vio[1]]++;
            for(int i=1;i<=n;i++)
                if(f[i]==1&&!pos[g[i]])
                    seg[pos[g[i]]=++id]=g[i];
            for(int i=1;i<=n;i++) pre[i]=(f[i]==1)?i:pre[i-1];
            for(int i=n;i>=1;i--) nxt[i]=(f[i]==1)?i:nxt[i+1];
            int l=0,r=id;
            for(int i=1;i<=tot;i++) {
                int u=vio[i];
                if(nxt[ql[u]]<=qr[u]) l=max(l,pos[g[nxt[ql[u]]]]);
                else l=id+1;
                if(pre[qr[u]]>=ql[u]) r=min(r,pos[g[pre[qr[u]]]]);
                else r=0;
            }
            for(int i=l;i<=r;i++) ans[seg[i]]++;
            for(int i=1;i<=K;i++) pos[i]=0;
        }
        for(int i=1;i<=K;i++)
            if(ans[i]==60) putchar('1');
            else putchar('0');
        puts("");
    }
    

    F. Border Similarity Undertaking

    给定一个字母矩形,求出其有多少的子矩形满足四周都是相同字符。

    (1le n,mle 2000)

    Solution

    考虑矩阵分治,即对一个矩阵的长宽进行分治。
    对于每一个矩阵进行分治,考虑计算跨过中心线的矩阵个数。
    这个过程可以预处理出一个点向上,向下,向左,向右的延伸距离,可以暴力处理。
    中心线左右两边分别处理,处理出左右两边能组成的矩形数。
    代码细节较多较复杂,时间复杂度 (O(nmlog n))

    Code
    const int N=2000;
    int n,m;
    char ch[N+10][N+10];
    int up[N+10][N+10],dn[N+10][N+10],lef[N+10][N+10],rig[N+10][N+10];
    int al[N+10][N+10],ar[N+10][N+10];
    ll ans;
    void solve(int l1,int r1,int l2,int r2) {
        if(l1==r1||l2==r2) return;
        if(r1-l1+1<=r2-l2+1) {
            int mid=(r2+l2)>>1,L,R;
            for(int i=l1;i<=r1;i++)
                for(int j=l1;j<=r1;j++)
                    al[i][j]=ar[i][j]=0;
            for(int i=l1;i<=r1;i++) {
                for(int j=mid;j>=l2&&j>=lef[i][mid];j--)
                    al[i][min(dn[i][j],r1)]++,
                    al[i][max(up[i][j],l1)]++;
                for(int j=r1-1;j>=i;j--) al[i][j]+=al[i][j+1];
                for(int j=l1+1;j<=i;j++) al[i][j]+=al[i][j-1];
                for(int j=mid+1;j<=r2&&j<=rig[i][mid+1];j++) 
                    ar[i][min(dn[i][j],r1)]++,
                    ar[i][max(up[i][j],l1)]++;
                for(int j=r1-1;j>=i;j--) ar[i][j]+=ar[i][j+1];
                for(int j=l1+1;j<=i;j++) ar[i][j]+=ar[i][j-1];
            }
            for(int i=l1;i<=r1;i++)
                for(int j=i+1;j<=r1;j++) {
                    if(ch[i][mid]!=ch[j][mid]) continue;
                    if(ch[i][mid]!=ch[i][mid+1]) continue;
                    if(ch[j][mid]!=ch[j][mid+1]) continue;
                    L=lef[i][mid]>=lef[j][mid]?al[i][j]:al[j][i];
                    R=rig[i][mid+1]<=rig[j][mid+1]?ar[i][j]:ar[j][i];
                    ans+=1ll*L*R;
                }
            solve(l1,r1,l2,mid);
            solve(l1,r1,mid+1,r2);
        }
        else {
            int mid=(l1+r1)>>1,L,R;
            for(int i=l2;i<=r2;i++)
                for(int j=l2;j<=r2;j++)
                    al[i][j]=ar[i][j]=0;
            for(int i=l2;i<=r2;i++) {
                for(int j=mid;j>=l1&&j>=up[mid][i];j--)
                    al[i][min(rig[j][i],r2)]++,
                    al[i][max(lef[j][i],l2)]++;
                for(int j=r2-1;j>=i;j--) al[i][j]+=al[i][j+1];
                for(int j=l2+1;j<=i;j++) al[i][j]+=al[i][j-1];
                for(int j=mid+1;j<=r1&&j<=dn[mid+1][i];j++)
                    ar[i][min(rig[j][i],r2)]++,
                    ar[i][max(lef[j][i],l2)]++;
                for(int j=r2-1;j>=i;j--) ar[i][j]+=ar[i][j+1];
                for(int j=l2+1;j<=i;j++) ar[i][j]+=ar[i][j-1];
            }
            for(int i=l2;i<=r2;i++)
                for(int j=i+1;j<=r2;j++) {
                    if(ch[mid][i]!=ch[mid][j]) continue;
                    if(ch[mid][i]!=ch[mid+1][i]) continue;
                    if(ch[mid][j]!=ch[mid+1][j]) continue;
                    L=up[mid][i]>=up[mid][j]?al[i][j]:al[j][i];
                    R=dn[mid][i]<=dn[mid][j]?ar[i][j]:ar[j][i];
                    ans+=1ll*L*R;
                }
            solve(l1,mid,l2,r2);
            solve(mid+1,r1,l2,r2);
        }
    }
    int main() {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                cin>>ch[i][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) {
                up[i][j]=i>1&&ch[i][j]==ch[i-1][j]?up[i-1][j]:i;
                lef[i][j]=j>1&&ch[i][j]==ch[i][j-1]?lef[i][j-1]:j;
            }
        for(int i=n;i>=1;i--)
            for(int j=m;j>=1;j--) {
                dn[i][j]=i<n&&ch[i][j]==ch[i+1][j]?dn[i+1][j]:i;
                rig[i][j]=j<m&&ch[i][j]==ch[i][j+1]?rig[i][j+1]:j;
            }
        solve(1,n,1,m);
        printf("%lld
    ",ans);
    }
    

    G. Biological Software Utilities

    求出存在完美匹配的 (n) 个点的树的数量。(1le nle 10^6)

    Solution

    把两个点绑一块,共有 (n!!=1 imes 3 imes ldots imes (n-1)) 种方法。
    绑一块的点能组成的树,有 ((frac{n}{2})^{n/2-2}) 种。
    点与点之间相互连接,有 (4^{n/2-1}) 种。
    乘起来,没了,时间复杂度 (O(n))

    Code
    const int N=1e6,mod=998244353;
    int n;
    int ans=1;
    int fpow(int x,int y) {
        if(y<0) return 1;
        int ret=1;
        for(;y;y>>=1) {
            if(y&1) ret=1ll*ret*x%mod;
            x=1ll*x*x%mod;
        }
        return ret;
    }
    int main() {
        scanf("%d",&n);
        if(n%2) {puts("0");return 0;}
        for(int i=1;i<=n;i+=2) ans=1ll*ans*i%mod;
        ans=1ll*ans*fpow(n/2,n/2-2)%mod*fpow(4,n/2-1)%mod;
        printf("%d
    ",ans);
    }
    

    H. Bytelandia States Union

    ((x_1,y_1)) 走到 ((x_2,y_2)),求最短路径。
    其中从 ((x,y)) 向四个方向走的方案不一,具体看原题。
    (1le Tle 5 imes 10^4)(1le x_1,y_1,x_2,y_2le 10^9)

    Solution

    诈骗题,我可以很负责任的告诉你,一条路径 ((x_1,y_1),(x_2,y_2),(x_3,y_3),ldots,(x_k,y_k)) 的费用是 (x^2_ky^2_k-x^2_1y^2_1-x_k^2-x_k^2+sum^{k}_{i=1}x^2_i+y^2_i),证明?可以问hht2005,总之我不会。
    最小化路径,只需最小化后面的值,所以只要尽可能挨近 (x=y) 这条直线就可以了。
    大力分讨即可,单次时间复杂度 (O(1))

    Code
    void solve() {
        int x1,y1,x2,y2;
        scanf("%lld %lld %lld %lld",&x1,&y1,&x2,&y2);
        ans=(p2(1ll*x2*y2%mod)-p2(1ll*x1*y1%mod)+mod)%mod;
        ans=(ans-p2(x2)-p2(y2)+mod)%mod;
        if(x1>x2) swap(x1,x2),swap(y1,y2);
        if(y2>y1)
            if(x1<y1) {
                int nx=min(y1,x2);
                Add(ans,1ll*(nx-x1)*p2(y1)%mod);
                Add(ans,sum(x1,nx-1));
                if(nx==x2) {
                    Add(ans,1ll*(y2-y1+1)*p2(x2)%mod);
                    Add(ans,sum(y1,y2));
                }
                else {
                    int nxt=min(x2,y2);
                    Add(ans,(sum(nx,nxt-1)*3ll%mod+sum(nx+1,nxt))%mod);
                    if(nxt==y2) {
                        Add(ans,1ll*p2(y2)*(x2-nxt+1)%mod);
                        Add(ans,sum(nxt,x2));
                    }
                    else {
                        Add(ans,1ll*p2(x2)*(y2-nxt+1)%mod);
                        Add(ans,sum(nxt,y2));
                    }
                }
            }
            else {
                int ny=min(x1,y2);
                Add(ans,1ll*(ny-y1)*p2(x1)%mod);
                Add(ans,sum(y1,ny-1));
                if(ny==y2) {
                    Add(ans,1ll*(x2-x1+1)*p2(y2)%mod);
                    Add(ans,sum(x1,x2));
                }
                else {
                    int nxt=min(x2,y2);
                    Add(ans,sum(ny,nxt-1)*2ll%mod);
                    Add(ans,sum(ny,nxt-1)+sum(ny+1,nxt));
                    if(nxt==x2) {
                        Add(ans,1ll*p2(x2)*(y2-nxt+1)%mod);
                        Add(ans,sum(nxt,y2));
                    }
                    else {
                        Add(ans,1ll*p2(y2)*(x2-nxt+1)%mod);
                        Add(ans,sum(nxt,x2));
                    }
                }
            }
        else {
            Add(ans,1ll*(y1-y2)*p2(x1)%mod);
            Add(ans,sum(y2+1,y1));
            Add(ans,1ll*(x2-x1+1)*p2(y2)%mod);
            Add(ans,sum(x1,x2));
        }
        printf("%lld
    ",(ans%mod+mod)%mod);
    }
    signed main() {
        ll t;scanf("%lld",&t);
        while(t--) solve();    
    }
    

    I. Binary Supersonic Utahraptors

    Alice 和 Bob 又在玩游戏,Alice 有黑与白两种物品,Bob 也有黑与白两种物品。

    (k) 个回合,每一回合中,Alice 先送给 Bob (s_i) 个物品,Bob 则回礼 (s_i) 个物品。

    Alice 想让 Alice 的黑物品与 Bob 的白物品差尽可能小,Bob 则想让上述东西最大。

    求最后的这个值。

    (1le n,m,kle 3 imes 10^5)

    Solution

    人定不胜天,Alice 和 Bob 并不能决定这一切。
    答案恒定不变,是总的黑物品数与 (n) 的差。
    时间复杂度 (O(1))

    Code
    #include<bits/stdc++.h>
    using namespace std;
    int n,m,k,r,y;
    int main() {
        scanf("%d %d %d",&n,&m,&k);
        for(int i=1,x;i<=n;i++) {
            scanf("%d",&x);
            if(x==1) r++;
        }
        for(int i=1,x;i<=m;i++) {
            scanf("%d",&x);
            if(x==1) r++;
        }
        printf("%d
    ",abs(n-r));
    }
    

    J. Burnished Security Updates

    求一张图的最小的覆盖所有边的独立集,(2le nle 3 imes 10^5)(1le mle 3 imes 10^5)

    Solution

    要覆盖所有边,所以相邻的两个点一定取得状态不同。
    考虑二分图染色,如果原图有奇环,则一定无解,否则取较小的那种颜色的点集。
    时间复杂度 (O(n+m))

    Code
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int N=3e5;
    int n,m,vis[N+10],cnt[4],ans;
    vector<int> G[N+10];
    bool dfs(int u,int col) {
        vis[u]=col;cnt[col]++;
        bool fl=1;
        for(auto v:G[u])
            if(!vis[v]) fl&=dfs(v,3-col);
            else if(vis[v]==col) return 0;
        return 1;
    }
    int main() {
        scanf("%d %d",&n,&m);
        for(int i=1,x,y;i<=m;i++) {
            scanf("%d %d",&x,&y);
            G[x].pb(y),G[y].pb(x);
        }
        for(int i=1;i<=n;i++)
            if(!vis[i]) {
                cnt[1]=cnt[2]=0;
                if(!dfs(i,1)) {
                    puts("-1");
                    return 0;
                }
                ans+=min(cnt[1],cnt[2]);
            }
        printf("%d
    ",ans);
    }
    

    K. Bookcase Solidity United

    (n) 块木板,第 (i) 块木板有稳定性 (a_i),木板从高到低排列。
    在木板上放置铱球,第 (i) 块木板被放了大于等于 (a_i) 个铱球后会碎裂,有 (lfloorfrac k 2 floor) 会下坠到下一块木板上。
    求至少需要多少铱球才可以砸碎前 (i) 块木板。
    (1le nle 70)(1le a_ile 150)

    Solution

    (f_{l,r,k}) 表示砸碎 ([l,r]) 的木板,留下 (k) 个球的最小球数。
    有一个显然的转移:(f_{l,r,k}+max(a_{r+1}-k,0) o f_{l,r+1,max(k,a_{r+1})/2}),表示继承下若干个球来砸下面的。
    同时,我们考虑分段砸,(f_{l,r,k_1+k_2}=f_{l,mid,k_1}+f_{mid+1,r,k_2})
    直接暴力转移是 (O(n^3m^2)) 的。

    Code
    #include<bits/stdc++.h>
    using namespace std;
    const int N=200,M=200;
    int n,a[N+10],m;
    int f[N+10][N+10][M+10];
    void chkmin(int &x,int y) {
        if(x>y) x=y;
    }
    int main() {
        scanf("%d",&n);
        memset(f,0x3f,sizeof f);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i][i][a[i]/2]=a[i],m=max(m,a[i]);
        for(int len=2;len<=n;len++)
            for(int l=1;l+len-1<=n;l++) {
                int r=l+len-1;
                for(int i=0;i<=m;i++) chkmin(f[l][r][max(i,a[r])/2],f[l][r-1][i]+max(0,a[r]-i));
                for(int i=0;i<=m;i++)
                    for(int j=l;j<r;j++)
                        for(int k=0;k<=i;k++)
                            chkmin(f[l][r][i],f[l][j][k]+f[j+1][r][i-k]);
            }
        for(int i=1;i<=n;i++) {
            int ans=INT_MAX;
            for(int j=0;j<=m;j++) ans=min(ans,f[1][i][j]);
            printf("%d ",ans);
        }
        puts("");
        return 0;
    }
    

    M. Brilliant Sequence of Umbrellas

    构造一个长度至少为 (lceil frac2 3 sqrt{n} ceil) 的值域为 ([1,n]) 递增序列,使得两两之间的 (gcd) 也递增。
    (1le nle 10^{12})

    Solution

    观察样例,考虑构造一个每隔两个数均互质的序列,然后将这个序列相邻两项的积作为原序列。
    这样一定满足 (gcd) 递增。

    Code
    typedef long long ll;
    const int N=1e6;
    ll n,a[N+10],cnt,b[N+10];
    int main() {
        scanf("%lld",&n);
        b[++cnt]=1,b[++cnt]=1;
        int lim=ceil(2.0*sqrt(n)/3);
        for(ll i=2;cnt<=lim+1&&b[cnt]*i<=n;i++)
            if(__gcd(b[cnt-1],i)==1)
                b[++cnt]=i;
        printf("%lld
    ",cnt-1);
        for(int i=1;i<cnt;i++) printf("%lld ",b[i]*b[i+1]);
        puts("");
    }
    

    N. Best Solution Unknown

    (n) 个人打比赛,每个人有个实力值,实力值大的会打败实力值小的,相等的都有机会赢。
    打赢一个人可以增加 (1) 的实力值,每个人只能和相邻的人打。
    每次随机取比赛,求会有哪些人可能赢。
    (1le nle 10^6)(1le a_ile 10^9)

    Solution

    考虑对于一个大段,一个人能不能赢,抽取出最大值,发现最大值分成两份,一份是左一份是右,要想赢得翻过最大值这座大山。
    那么我们就可以分治了,找出最大值来分治,每次记录左边或右边要至少多少才可以赢。
    分治的时候用 ST 表加速求最值就可以了。

    Code
    const int N=1e6;
    int a[N+10],n,Log[N+10],Max[N+10][30],id[N+10][30];
    bool nb[N+10];
    int query(int l,int r) {
        int k=Log[r-l+1];
        int ret=max(Max[l][k],Max[r-(1<<k)+1][k]);
        if(ret==Max[l][k]) return id[l][k];
        else return id[r-(1<<k)+1][k];
    }
    void dfs(int l,int r,int c) {
        if(l>r) return;
        if(l==r) {
            if(a[l]>=c) nb[l]=1;
            return;
        }
        int id=query(l,r);
        if(a[id]<c) return;
        nb[id]=1;
        dfs(l,id-1,max(c,a[id]-id+1+l));
        dfs(id+1,r,max(c,a[id]+id+1-r));
    }
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        Log[1]=0;
        for(int i=2;i<=n;i++) Log[i]=Log[i/2]+1;
        for(int i=1;i<=n;i++) Max[i][0]=a[i],id[i][0]=i;
        for(int j=1;j<=20;j++)
            for(int i=1;i+(1<<(j-1))<=n;i++) {
                Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);
                if(Max[i][j]==Max[i][j-1]) id[i][j]=id[i][j-1];
                else id[i][j]=id[i+(1<<(j-1))][j-1];
            }
        dfs(1,n,0);
        int cnt=0;
        for(int i=1;i<=n;i++) if(nb[i]) cnt++;
        printf("%d
    ",cnt);
        for(int i=1;i<=n;i++) if(nb[i]) printf("%d ",i);
        puts("");
    }
    
  • 相关阅读:
    样式超出设定宽度显示显示省略号
    客户端存在潜在危险request.from
    MenuStrip如何设置快捷键
    SVN的使用方法
    长串英文字符不换行的解决办法
    thickbox使用
    System.Web.UI.UserControl”,因此此处不允许
    C#中实现拖动无边框Form窗体和窗体的起始位置
    (转)Altera Forum精彩问答汇总
    (转)如何以32 bit的方式存取SDRAM?
  • 原文地址:https://www.cnblogs.com/lajiccf/p/15424408.html
Copyright © 2020-2023  润新知