AGC/ARC

AGC043

范围很小, 直接$O(n^4)dp$

B

题意 给定长$n$的序列$a$, 只含$1,2,3$. 有$f_{k,x}=|f_{k-1,x}-f_{k-1,x+1}|,f_{1,x}=a_x$. 求$f_{n,1}$

  • 先特判掉$n=1$的情况, 那么答案只能为$0,1,2$
  • 答案的奇偶性与$\sum a_i \binom{n-1}{i-1}$相同
  • $\binom{n}{m}$为奇就等价于$n|m=n$, 可以用$Lucas$定理证明
  • 判断是$0$还是$2$, 可以全除以$2$后用相同方法判断

C

题意 给定$3$个图$X,Y,Z$, 节点数均为$n$. 构造一个$n^3$节点的图$W$, 每个点记做$(x,y,z)$

AGC044

A 考虑从$N$转移到$0$的最少花费, 可以发现每次增加时至多增加到$2,3,5$的倍数, 然后记忆化搜索即可

B 每走一个人就更新最短路, 复杂度是O(N^3)
C 从低位到高位建三进制trie, $S$操作相当于全局交换子树, $R$操作将$0$换为$1$,$1$换为$2$,$2$换为$0$继续往高位处理

题意 交互题, 要猜一个字符串$S$, 每次可以询问一个串$T$, 返回$S$和$T$的最短编辑距离

  • 核心观察是$T$是$S$的子序列等价于最短编辑距离为$|S|-|T|$
  • 先对每个字符询问,求出每种字符个数,然后按照哈弗曼树合并每个串即可

E

F 

NOMURA Programming Competition 2020

AB 签到

C 核心观察是当前层非叶节点数不超过更深的叶子数的和

  • 转化为求总连通块数. $-1$所在连通块一定是树, 其余连通块一定是环套树
  • 假设有$cnt$个环套树, $m$个树, 显然环套树贡献不会变, 贡献为$cnt (n-1)^m$
  • 对于树的贡献, 假设选$k$棵树连成连通块
  • $k=1$时, 贡献为$(n-1)^{m-1}\sum ({sz}_i-1) $
  • $k>1$时, 贡献为$(k-1)!(n-1)^{m-k}\sum\prod {sz}_i$

题意 给定串$T$, 串$S$初始为空, 每次操作在$S$中添加$0$或$1$, 贡献加上$S$奇数位的$1$, 求$S$等于$T$时的最大贡献

考虑逆序操作, 先删$0$一定最优. 考虑从前往后删$0$, 如果$0$后面的一段连续$1$为奇数, 就通过留一个$0$或者不留$0$, 把连续$1$调整到奇数位. 如果后面连续$1$为偶数, 那么$0$全删. 最后再从后往前删掉所有$0$即可

F

一个序列$a$可以排序, 就等价于对于每对$i,j(i<j)$, 若$a_i$和$a_j$异或和不为$2$的幂, 那么$a_i<a_j$

那么一个序列$a$合法, 就等价于对于每对$i,j(i<j)$, 从高位到低位, 第一次出现$a_i$为$1$且$a_j$为$0$后, $a_i$和$a_j$的低位都相等

从高位到低位考虑, 假设存在$i,j(i<j)$, 满足$a_i$为$1$且$a_j$为$0$, 那么$[i,j]$区间内每一个数低位都应该相等, 所以$[i,j]$区间就可以缩为一个数, 枚举区间$[i,j]$的长度, 就有方程

$f_{N,M}=(M+1)f_{N-1,M}+\sum\limits_{x=2}^M(M-x+1)2^{x-2}f_{N-1,M-x+1}$

前缀优化就可以$O(NM)$

AGC045 

A $0$的每个后缀线性基都能异或出对应后缀的所有$1$, 那么答案为$0$, 否则为$1$

B

  • $0$看成$-1$,转化成求最大子段和的最小值.
  • 假设前缀和最小值为$M$时前缀和最大值为$f(M)$, $Z$为$M$的最大值
  • 答案就为$min\{f(M)-M :M\le Z\}$
  • $M$增大$2$时,$f(M)$至多增大$2$, 最小值一定为$f(Z)-Z$或$f(Z-1)-Z+1$

C

  • 假设$a<b$, 合法串等价于把所有长度$\ge a$的$0$替换为$1$后存在长度$\ge b$的$1$
  • 考虑不合法方案, 一定是由$<a$的$0$和$<b$的$1$连接起来, 并且每段$1$中可以任意替换$\ge a$的$0$
  • 预处理每种长度的$1$替换$\ge a$的$0$的方案, 然后$O(N^2)DP$

D 

Tokio Marine & Nichido Fire Insurance Programming Contest 2020

E

F

 

 

 

ARC 104

C

合法状态可以分成若干区间, 每段区间为$(l,l+k),(l+1,l+k+1),...,(l+k-1,l+2k-1)$

可以$dp$判断 

#include <bits/stdc++.h>
using namespace std;
const int N = 210;
int n, a[N], b[N], vis[N], dp[N];
int main() {
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) scanf("%d%d", a+i, b+i);
    dp[0] = 1;
    for (int i=1; i<=n; ++i) {
        if (a[i]>0) {
            if (vis[a[i]]) dp[0] = 0;
            vis[a[i]] = i;
        }
        if (b[i]>0) {
            if (vis[b[i]]) dp[0] = 0;
            vis[b[i]] = i;
        }
        if (a[i]>0&&b[i]>0&&a[i]>=b[i]) dp[0] = 0;
    }
    for (int i=1; i<=2*n; ++i) if (dp[i-1]) {
        for (int j=i+1; j<=2*n; j+=2) {
            int len = (j-i+1)/2, ok = 1;
            for (int k=i; k<i+len; ++k) if (vis[k]) {
                int x = vis[k];
                if (a[x]>0&&a[x]!=k||b[x]>0&&b[x]!=k+len) ok = 0;
                if (vis[k+len]&&vis[k]!=vis[k+len]) ok = 0;
            }
            for (int k=i+len; k<i+2*len; ++k) if (vis[k]) {
                int x = vis[k];
                if (a[x]>0&&a[x]!=k-len||b[x]>0&&b[x]!=k) ok = 0;
            }
            if (ok) dp[j] = 1;
        }
    }
    puts(dp[2*n]?"Yes":"No");
}
View Code

题意 给定$n,k$, 对于$1\le x\le n$, 求出从$k$重集$\{1,2,...,n\}$中取出平均值为$x$的方案数

平均数为$x$可以转化为$<x$的数到$x$的距离和等于$>x$的数到$x$的距离和

就转化为从集合$\{1,2,...,x-1\}$与$\{1,2,...,n-x\}$中选出和相同的方案

设${dp}_{i,j}$表示前$i$个数, 每种数最多取$k$个, 和为$j$的方案

${dp}_{i,j}={dp}_{i-1,j}+{dp}_{i-1,j-i}+...+{dp}_{i-1,j-k*i}$

可以发现转移时模$i$相同, 前缀优化一下, 复杂度$O(n^3 k)$

#include <bits/stdc++.h>
using namespace std;
int dp[105][495005];
int main() {
    int n, k, m;
    scanf("%d%d%d", &n, &k, &m);
    dp[0][0] = 1;
    int mx = (n-1)*n/2*k;
    for (int i=1; i<n; ++i) {
        for (int j=0; j<=mx; ++j) {
            dp[i][j] = dp[i-1][j];
            if (j>=i) dp[i][j] = (dp[i][j]+dp[i][j-i])%m;
        }
        for (int j=mx; j>=0; --j) {
            if (j>=(k+1)*i) dp[i][j] = (dp[i][j]-dp[i][j-(k+1)*i])%m;
        }
    }
    for (int i=1; i<=n; ++i) {
        int ans = 0;
        for (int j=0; j<=mx; ++j) { 
            ans = (ans+(long long)dp[i-1][j]*dp[n-i][j])%m;
        }
        ans = (ans*(k+1ll)-1)%m;
        if (ans<0) ans += m;
        printf("%d\n", ans);
    }
}
View Code

E

题意 给定序列$A$, 序列$X_i$在$[1,A_i]$中随机取, 求$X$的期望$LIS$长度

$O(n!)$枚举排列$p$表示每个数大小关系$a_{p1}<a_{p2}<...<a_{pn}$

但是可能存在有数相等的情况, 解决方法是当$p_i<p_{i+1}$时取$<$, $p_i>p_{i+1}$时取$\le$

显然这样可以不重不漏, 并且$LIS$的长度不变

然后$dp$即可, 总复杂度$O(n! n^3)$

#include <bits/stdc++.h>
using namespace std;
const int N = 10, P = 1e9+7;
int n, a[N], b[N], p[N], dp[N][N], f[N], inv[N];
int qpow(long long a, int n) {
    long long ans = 1;
    for (; n; a=a*a%P,n>>=1) if (n&1) ans=ans*a%P;
    return ans;
}
int main() {
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) scanf("%d", a+i), b[i] = a[i];
    sort(b+1, b+n+1);
    for (int i=1; i<=n; ++i) p[i] = i, inv[i] = qpow(i, P-2);
    int ans = 0;
    do {
        memset(dp,0,sizeof dp);
        dp[0][0] = 1;
        //dp[i][j] = 把[1,b[i]]填到X[p1],X[p2],...,X[pj]
        //满足若p[k]<p[k+1]则X[pk]<X[p(k+1)], 若p[k]>p[k+1]则X[pk]<=X[p(k+1)]的方案
        for (int i=1; i<=n; ++i) {
            int L = b[i-1], R = b[i];
            memcpy(dp[i],dp[i-1],sizeof dp[0]);
            for (int j=1; j<=n; ++j) {
                int r = dp[i-1][j-1];
                if (!r) continue;
                int cnt = 0;
                //cnt记录<=的个数, dp[i][k] += dp[i-1][j-1]*C(R-L+cnt,k-j+1)
                for (int k=j; k<=n; ++k) {
                    if (a[p[k]]<R) break;
                    if (k>j&&p[k]<p[k-1]) { 
                        ++cnt;
                        r = (long long)r*(R-L+cnt)%P*qpow(R-L+cnt-k+j,P-2)%P;
                    }
                    r = (long long)r*(R-L+cnt-k+j)%P*inv[k-j+1]%P;
                    if (r==0) break;
                    dp[i][k] = (dp[i][k]+r)%P;
                }
            }
        }
        memset(f,0,sizeof f);
        int len = 0;
        for (int i=1; i<=n; ++i) {
            for (int j=1; j<i; ++j) if (p[i]>p[j]) f[i] = max(f[i], f[j]);
            len = max(len, ++f[i]);
        }
        ans = (ans+(long long)len*dp[n][n])%P;
    } while (next_permutation(p+1, p+n+1));
    for (int i=1; i<=n; ++i) ans = (long long)ans*qpow(a[i], P-2)%P;
    printf("%d\n", ans);
}
View Code

 

posted @ 2020-06-13 11:49  dz8gk0j  阅读(283)  评论(0编辑  收藏  举报