• OI Memory 后记——「新征程、就从明天开始」


    2021.7.31

    上午把回忆录写完了,不太想看深度学习,就搞搞谔谔模拟赛。

    2021.8.6

    滚来参加 tx 的一个活动,zjk、cyx、cxr 都来了,邓老师被请过来装学员 /kx

    下午坐高铁到了深圳,等 zz 等了 1h,就滚去酒店了。

    晚上搞了一些谔谔的团建活动,但通信题还是比超脑好玩多了,虽然我啥都没干的团队任务又让我意识到了我是个 fw 的事实。

    题目就是通过 10 根绳子传递长度不超过 10 的字符串,字符可能是小写字母、数字、数学符号、常见汉字。

    邓老师造的编码方法是一个绳子表示一个字符,穿一次的结表示 0,穿两次的结表示 1,类型用 2bit,然后直接对汉字之外的字符编码,汉字直接写拼音。

    zz 他们队的编码方法要劣一些,但他们打结技术高超所以 rk1 了,ntf 他们队也 rk2 了,我们即使有邓老师带还是垫底了。

    2021.8.7

    听了一天课,无聊死了。

    晚上坐双层巴士乱逛,导游的声音有点 /tuu。

    2021.8.12

    总体来说体验很差,因为实在不想做任务。

    终于熬到结束可以放假了。

    2021.9.1

    上午无聊就在数竞室看小蓝皮(

    ARC122E Increasing LCMs

    给定长为 (n) 的正整数列 (x_i),求重排方案使得前缀 LCM 递增。

    (nle 100)(x_ile 10^{18})


    从后往前考虑,对于末尾的数 (x_i),要满足 (x_i mid ext{LCM}_{j e i}(x_j))

    为了方便判断考虑转化一下:( ext{LCM}_{j e i}(gcd(x_j,x_i))<x_i)

    同时对于可以放在末尾的数 (x_i),它放在前面不会更优,于是就放末尾了。

    暴力判断,时间复杂度 (O(n^3log V))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 103;
    int n;
    LL a[N], ans[N];
    bool vis[N];
    LL gcd(LL a, LL b){return b ? gcd(b, a % b) : a;}
    LL lcm(LL a, LL b){return a / gcd(a, b) * b;}
    int main(){
        ios::sync_with_stdio(false);
        cin >> n;
        for(int i = 1;i <= n;++ i) cin >> a[i];
        for(int p = n;p;-- p){
            bool flg = false;
            for(int i = 1;i <= n;++ i) if(!vis[i]){
                LL tmp = 1;
                for(int j = 1;j <= n;++ j)
                    if(!vis[j] && i != j) tmp = lcm(tmp, gcd(a[j], a[i]));
                if(tmp < a[i]){ans[p] = a[i]; vis[i] = flg = true; break;}
            }
            if(!flg){puts("No"); return 0;}
        }
        puts("Yes");
        for(int i = 1;i <= n;++ i) printf("%lld ", ans[i]);
    }
    

    ARC123E Training

    给定正整数 (a_1,a_2,b_1,b_2,n),求

    [sum_{i=1}^nleft[a_1+lfloorfrac i{b_1} floor=a_2+lfloorfrac{i}{b_2} floor ight] ]

    (T) 组数据。(Tle 2cdot 10^5)(nle 10^9)(a_1,a_2,b_1,b_2le 10^6)


    先转化为 (lfloor i/b_1 floor-lfloor i/b_2 floor=a_2-a_1),设为 (a)

    先特判掉 (b_1=b_2) 的情况,不妨设 (b_1<b_2)

    显然可以先圈一圈范围:(i/b_1-i/b_2in(-1,1])

    乱搞一下,分成 (i/b_1-i/b_2in(-1,0])(i/b_1-i/b_2in(0,1])。此时计算一下 (sumlfloor i/b_1 floor-sumlfloor i/b_2 floor) 就可以求出对应的答案。

    时间复杂度 (O(T))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    int T;
    LL n, a, b, c, d;
    LL calc(LL a, LL b){LL c = a / b; return (c*(c-1ll)>>1)*b + (a-b*c)*c;}
    LL calc(LL l, LL r, LL a, LL b){return a*(r-l) + calc(r,b) - calc(l,b);}
    int main(){
        ios::sync_with_stdio(false);
        cin >> T;
        while(T --){
            cin >> n >> a >> b >> c >> d;
            if(b == d){printf("%lld
    ", a == c ? n : 0); continue;}
            if(b > d){swap(a, c); swap(b, d);} c -= a;
            LL ans = 0, l = max(b*d*(c-1)/(d-b),0ll) + 1, r = min(b*d*c/(d-b),n) + 1;
            if(l < r) ans = r - l + calc(l, r, 0, b) - calc(l, r, c, d);
            l = max(b*d*c/(d-b),0ll) + 1; r = min(b*d*(c+1)/(d-b),n) + 1;
            if(l < r) ans += r - l + calc(l, r, c, d) - calc(l, r, 0, b);
            printf("%lld
    ", ans);
        }
    }
    

    ARC124E Pass to Next

    给定 (n) 个正整数,求

    [sum_{x_ile a_i \ min x_i=0}prod_{i=1}^n(a_i-x_i+x_{i-1})mod 998244353 ]

    (nle 10^5)(a_ile 10^9)


    (min x_i=0) 的限制很容易去掉,只需要减去限制传球数 (>0) 的情况即可。

    考虑组合意义,传完球后每个人要取出自己的一个球。

    因为每个人的传球数互相独立,所以对每个人取出球的来源做 dp。设 (f_{i,0}) 表示第 (i) 个人取出自己的球的情况下,前 (i-1) 个人取球的方案数,(f_{i,1}) 表示第 (i) 个人取出第 (i-1) 个人给他的球的情况下,前 (i) 个人取球的方案数。

    (S_k(n)=sum_{i=0}^ni^k),则可以列出转移式:

    [egin{aligned} f_{i+1,0}&=S_1(a_i)cdot f_{i,0}+(a_i+1)cdot f_{i,1} \ f_{i+1,1}&=(a_iS_1(a_i)-S_2(a_i))cdot f_{i,0}+S_1(a_i)cdot f_{i,1} end{aligned} ]

    如果限制传球数 (>0) 就将第一个柿子的 (a_i) 减去 (1)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 100003, mod = 998244353, inv3 = (mod+1)/3;
    int n, a[N], f[N][2], ans;
    void qmo(int &x){x += x >> 31 & mod;}
    int calc(bool f1, bool f2){
        memset(f, 0, n+1<<3); f[0][f1] = 1;
        for(int i = 0;i < n;++ i){
            int b = a[i]-f2, c = (b*(b+1ll)>>1)%mod;
            f[i+1][0] = ((LL)c*f[i][0]+(b+1ll)*f[i][1])%mod;
            b += f2; c = (b*(b+1ll)>>1)%mod;
            int d = c*(b-1ll)%mod*inv3%mod;
            f[i+1][1] = ((LL)c*f[i][1]+(LL)d*f[i][0])%mod;
        } return f[n][f1];
    }
    int main(){
        ios::sync_with_stdio(false);
        cin >> n;
        for(int i = 0;i < n;++ i) cin >> a[i];
        qmo(ans += calc(0,0)+calc(1,0)-mod);
        qmo(ans -= calc(0,1)); qmo(ans -= calc(1,1));
        printf("%d
    ", ans);
    }
    

    ARC125E Snack

    给定 (n) 种零食,第 (i) 种零食有 (a_i) 份。有 (m) 个小朋友分零食,第 (i) 个小朋友至多得到 (c_i) 份零食,至多得到 (b_i) 份同一种零食。求最多可以分多少份零食。

    (n,mle2cdot 10^5)(a_i,c_ile 10^{12})(b_ile 10^7)


    这显然是最大流模型,转化为最小割之后直接做。时间复杂度 (O(nlog n+m))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 200003;
    template<typename T>
    bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
    int n, m;
    LL a[N], b[N], c[N], d[N], e[N], tmp1, tmp2, ans = 1e18;
    int main(){
        ios::sync_with_stdio(false);
        cin >> n >> m;
        for(int i = 1;i <= n;++ i) cin >> a[i];
        for(int i = 1;i <= m;++ i) cin >> b[i];
        for(int i = 1;i <= m;++ i){
            cin >> c[i]; tmp2 += c[i];
            int x = max(n - c[i] / b[i], 0ll);
            d[x] += b[i]; e[x] += c[i];
        }
        sort(a + 1, a + n + 1);
        for(int i = 0;i <= n;++ i){
            tmp2 += a[i] - e[i]; tmp1 += d[i];
            chmin(ans, tmp2 + tmp1 * (n-i));
        }
        printf("%lld", ans);
    }
    

    ARC125F Tree Degree Subset Sum

    给定 (n) 个点的树,求有多少对二元组 ((x,y)),满足存在一种选出 (x) 个点的方案,使得度数之和为 (y)

    (nle 2cdot 10^5)


    实际上这棵树并没有什么用,我们只需要度数序列 (d_1,d_2,cdots,d_n),满足 (sum d_i=2n-2)(d_ige 1)。除了这个性质之外就没有别的了。

    所以考虑随便打表猜结论,然后什么都看不出来。

    乱搞一下,发现把 (d_i) 都减去 (1) 没什么区别,于是就继续打表。

    发现若 (m(s),M(s)) 分别表示选出 (d_i) 之和为 (s) 的点数的最小值和最大值,则 ([m(s),M(s)]) 的点数都可以选出来。

    如果这个结论是对的,那么就不用对点数记状态了,直接大力背包即可。时间复杂度 (O(nsqrt n))

    题解告诉我们这个结论确实是对的。考虑设 (z=sum[d_i=0]),则对于任意子集 (Ssubseteq [n])(-zle ext{sum}(S)-|S|le z-2),所以 (M(s)-m(s)le 2z-2),而 (m(s)) 的方案不选 (0),如果选上可以得到 ([m(s),m(s)+z]) 的方案,(M(s)) 同理可得 ([M(s)-z,M(s)]) 的方案,得证。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 200003;
    int n, z, d[N], f[N];
    LL ans;
    template<typename T>
    bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
    template<typename T>
    bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
    int main(){
        ios::sync_with_stdio(false);
        cin >> n;
        memset(f, 0x3f, sizeof f); f[0] = 0;
        for(int i = 0;i < n;++ i) d[i] = -1;
        for(int i = n-1<<1, x;i;-- i){cin >> x; ++ d[x-1];}
        sort(d, d + n);
        for(int i = 0, j;i < n;i += j){
            for(j = 0;d[i+j] == d[i];++ j);
            int rem = j;
            for(int k = 1;k <= rem;rem -= k, k <<= 1){
                int val = d[i]*k;
                for(int i = n-2;i >= val;-- i)
                    chmin(f[i], f[i-val]+k);
            }
            if(rem){
                int val = d[i]*rem;
                for(int i = n-2;i >= val;-- i)
                    chmin(f[i], f[i-val]+rem);
            }
        }
        for(int i = 0;i < n-1;++ i) ans += max(0, n+1-f[n-2-i]-f[i]);
        printf("%lld
    ", ans);
    }
    

    CF1556F Sports Betting

    给定 (n) 个点的竞赛图,每个点 (i) 有点权 (a_i)(i o j) 的概率是 (frac{a_i}{a_i+a_j}),求能直接或间接到达所有点的点数期望值(mod(10^9+7))

    (nle 14)(a_ile 10^6)


    根据经典套路,竞赛图缩点之后是链,所以直接枚举入度为 (0) 的强连通分量 (S),计算 (|S|f_Sg_{S,Vackslash S}) 之和,其中 (f_S) 表示 (S) 是强连通图的概率,(g_{S,T}=prod_{iin S}prod_{jin T}frac{a_i}{a_i+a_j}) 表示所有 (S,T) 之间的边的方向都是 (S o T)

    先考虑计算 (f_S),直接容斥,如果不是强连通图,枚举入度为 (0) 的强连通分量 (T),则有

    [f_S=1-sum_{Tsubseteq S}f_Tg_{T,Sackslash T} ]

    (g) 的时候先对所有 ((x,T)) 预处理 (prod_{yin T}a_x/(a_x+a_y)) 就可以做到 (O(n3^n))

    题解是 (O(3^n)) 做法。直接折半,分别将 (S,T) 拆成两部分得到四项,算 (g) 的复杂度就直接降到 ( ext{soft-}O(2^n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1<<14, mod = 1e9+7;
    int n, n1, n2, l1, l2, lim, a[14], e[14][14], f[4][128][128], dp[N], ans;
    void qmo(int &x){x += x >> 31 & mod;}
    int ksm(int a, int b){
        int res = 1;
        for(;b;b >>= 1, a = (LL)a * a % mod)
            if(b & 1) res = (LL)res * a % mod;
        return res;
    }
    int calc(int S, int T){
        return (LL)f[0][S&l1-1][T&l1-1]*f[1][S&l1-1][T>>n1]%mod*f[2][S>>n1][T&l1-1]%mod*f[3][S>>n1][T>>n1]%mod;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin >> n; n1 = n>>1; n2 = n+1>>1; l1 = 1<<n1; l2 = 1<<n2; lim = 1<<n;
        for(int i = 0;i < n;++ i){
            cin >> a[i];
            for(int j = 0;j < i;++ j){
                e[i][j] = (LL)a[i]*ksm(a[i]+a[j],mod-2)%mod;
                qmo(e[j][i] = mod+1-e[i][j]);
            }
        }
        for(int S = 0;S < l1;++ S)
            for(int T = 0;T < l1;++ T) if(!(S&T)){
                f[0][S][T] = 1;
                for(int i = 0;i < n1;++ i) if(S>>i&1)
                    for(int j = 0;j < n1;++ j) if(T>>j&1)
                        f[0][S][T] = (LL)f[0][S][T] * e[i][j] % mod;
            }
        for(int S = 0;S < l2;++ S)
            for(int T = 0;T < l2;++ T) if(!(S&T)){
                f[3][S][T] = 1;
                for(int i = 0;i < n2;++ i) if(S>>i&1)
                    for(int j = 0;j < n2;++ j) if(T>>j&1)
                        f[3][S][T] = (LL)f[3][S][T] * e[i+n1][j+n1] % mod;
            }
        for(int S = 0;S < l1;++ S)
            for(int T = 0;T < l2;++ T){
                f[1][S][T] = 1;
                for(int i = 0;i < n1;++ i) if(S>>i&1)
                    for(int j = 0;j < n2;++ j) if(T>>j&1)
                        f[1][S][T] = (LL)f[1][S][T] * e[i][j+n1] % mod;
            }
        for(int S = 0;S < l2;++ S)
            for(int T = 0;T < l1;++ T){
                f[2][S][T] = 1;
                for(int i = 0;i < n2;++ i) if(S>>i&1)
                    for(int j = 0;j < n1;++ j) if(T>>j&1)
                        f[2][S][T] = (LL)f[2][S][T] * e[i+n1][j] % mod;
            }
        for(int i = 1;i <= n;++ i){
            for(int S = 1;S < lim;++ S) if(__builtin_popcount(S) == i){
                dp[S] = 1;
                for(int T = S&S-1;T;T = T-1&S)
                    qmo(dp[S] -= (LL)dp[T]*calc(T,S-T)%mod);
                ans = (ans + (LL)dp[S]*i%mod*calc(S,lim-1-S)) % mod;
            }
        }
        printf("%d
    ", ans);
    }
    

    2021.9.2

    ARC124F Chance Meeting

    给定 (n imes m) 的网格图,初始时骆驼在 ((1,1)),猫猫在 ((n,1)),每次操作骆驼可以向右/下走,猫猫可以往右/上走。求满足以下条件的操作序列个数(mod 998244353)

    • 骆驼最后走到 ((n,m)),猫猫最后走到 ((1,m))
    • 存在恰好一个操作,这个操作之后骆驼和猫猫在同一个格子上。

    (2le n,mle 2cdot 10^5)


    完全不会,滚去看题解了

    先将 (n,m) 都减去 (1),即两者都需做 (n) 次竖直移动和 (m) 次水平移动。

    发现只考虑竖直移动时,不关心移动两者中的哪个,因为它们在同一行当且仅当进行了 (n) 次竖直移动。

    (f_i) 表示两者在 (i) 次水平移动之后第一次相遇的方案数,则答案是 (inom{2n}{n}sum_{i=0}^mf_if_{m-i})

    (f) 是经典容斥,设 (g_i) 表示两者在 (i) 次水平移动之后相遇的方案数,则 (g_i=frac{(2i+n)!}{i!^2n!}),枚举两者倒数第二次相遇的位置 (j) 就得到:

    [f_i=g_i-2sum_{j=0}^{i-1}g_jC_{i-j-1} ]

    其中 (C_i) 是 Catalan 数,所以做一次卷积就算出来了,时间复杂度 (O(n+mlog m))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1<<19, M = 600003, mod = 998244353;
    int n, m, lim, A[N], B[N], C[N], rev[N], w[N], fac[M], inv[M], ans;
    int ksm(int a, int b){
        int res = 1;
        for(;b;b >>= 1, a = (LL)a * a % mod)
            if(b & 1) res = (LL)res * a % mod;
        return res;
    }
    void init(int n){
        fac[0] = 1;
        for(int i = 1;i <= n;++ i) fac[i] = (LL)fac[i-1] * i % mod;
        inv[n] = ksm(fac[n], mod-2);
        for(int i = n;i;-- i) inv[i-1] = (LL)inv[i] * i % mod;
    }
    void qmo(int &x){x += x >> 31 & mod;}
    void calrev(int len){
        int L = -1; lim = 1;
        while(lim <= len){lim <<= 1; ++ L;}
        for(int i = 0;i < lim;++ i)
            rev[i] = (rev[i>>1]>>1) | ((i&1)<<L);
        for(int i = 1;i < lim;i <<= 1){
            int wn = ksm(3, (mod-1)/(i<<1)); w[i] = 1;
            for(int j = 1;j < i;++ j) w[i+j] = (LL)w[i+j-1]*wn%mod;
        }
    }
    void NTT(int *A, bool op){
        for(int i = 0;i < lim;++ i)
            if(i < rev[i]) swap(A[i], A[rev[i]]);
        for(int md = 1;md < lim;md <<= 1)
            for(int i = 0;i < lim;i += md<<1)
                for(int j = 0;j < md;++ j){
                    int y = (LL)A[md+i+j]*(op&&j?mod-w[(md<<1)-j]:w[md+j])%mod;
                    qmo(A[md+i+j] = A[i+j] - y); qmo(A[i+j] += y-mod);
                }
        if(op){
            int inv = ksm(lim, mod-2);
            for(int i = 0;i < lim;++ i)
                A[i] = (LL)A[i] * inv % mod;
        }
    }
    int main(){
        ios::sync_with_stdio(false);
        cin >> n >> m; -- n; -- m;
        init(max(n,m<<1)+n); calrev(m<<1);
        for(int i = 0;i <= m;++ i) A[i] = B[i] = (LL)fac[2*i+n]*inv[i]%mod*inv[i]%mod*inv[n]%mod;
        for(int i = 0;i < m;++ i) C[i+1] = 2ll*fac[2*i]%mod*inv[i]%mod*inv[i+1]%mod;
        NTT(B, 0); NTT(C, 0);
        for(int i = 0;i < lim;++ i) B[i] = (LL)B[i] * C[i] % mod;
        NTT(B, 1);
        for(int i = 0;i <= m;++ i) qmo(A[i] -= B[i]);
        for(int i = 0;i <= m;++ i) ans = (ans + (LL)A[i]*A[m-i]) % mod;
        printf("%lld
    ", (LL)ans*fac[2*n]%mod*inv[n]%mod*inv[n]%mod);
    }
    

    2021.9.3

    ARC123F Insert Addition

    对于序列 (A={a_1,a_2,cdots,a_n}),定义 (f(A)={a_1,a_1+a_2,a_2,a_2+a_3,cdots,a_{n-1}+a_n,a_n})

    给定三个正整数 (a,b,n,l,r),设 (A={a,b})(B)(f^{(n)}(A)) 去掉 (>n) 的数之后得到的序列,求 (b_l,b_{l+1},cdots,b_r)

    (a,ble nle 3cdot 10^5)(lle rle 10^{18})(r-lle 3cdot 10^5)


    发现 (a,b) 的系数就是 Stern-Brocot 树的前 (n) 层的中序遍历。

    实际上第 (n) 层之后的数就不可能 (le n) 了,所以 (B) 就是所有 (xperp yland ax+byle n)((x,y))(x/y) 排序之后的结果。

    注意到 (f^{(n)}(A)=f^{(n-1)}({a,a+b})-{a+b}+f^{(n-1)}({a+b,b})) 的递归性质,于是就可以直接在 SB 树上二分,需要的就是求一棵子树的大小,直接 Mobius 反演即可。

    暴力实现的复杂度是大约是 2log,整除分块套类欧是 ( ext{soft-}O(sqrt n)) 的。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 300003, M = 26000;
    int a, b, n, tot, pri[M], mu[N];
    bool notp[N]; LL l, r;
    void print(int x){
        if(l > r) return; -- r;
        if(l == 1){printf("%d ", x); return;} -- l;
    }
    void print(int a, int b){if(a + b <= n){print(a, a + b); print(a + b); print(a + b, b);}}
    LL calc(int a, int b){
        if(a < b) swap(a, b);
        LL res = 0;
        for(int i = 1, tmp;a+b <= (tmp = n/i);++ i) if(mu[i])
            for(int j = 1;a*j <= tmp;++ j)
                res += (tmp - a*j) / b * mu[i];
        return res;
    }
    void work(int a, int b){
        if(l > r || a + b > n) return;
        LL c = calc(a, b);
        if(l > c){l -= c; r -= c; return;}
        if(l == 1 && c <= r){print(a, b); return;}
        work(a, a + b); print(a + b); work(a + b, b);
    }
    int main(){
        ios::sync_with_stdio(false);
        cin >> a >> b >> n >> l >> r;
        mu[1] = 1; notp[0] = notp[1] = true;
        for(int i = 2;i <= n;++ i){
            if(!notp[i]){pri[tot++] = i; mu[i] = -1;}
            for(int j = 0;j < tot && i * pri[j] <= n;++ j){
                notp[i * pri[j]] = true;
                if(i % pri[j]) mu[i * pri[j]] = -mu[i];
                else break;
            }
        }
        print(a); work(a, b); print(b);
    }
    

    2021.9.5

    上午开了场 vp,果然又因为沙雕错误卡题了 qwq

  • 相关阅读:
    mysql 优化(包含sql语句的书写)
    tomcat优化
    MySQL——修改root密码的4种方法(以windows为例)
    实现窗口中的文档自动向上滚动,方便阅读
    处理文本框的鼠标事件,判断鼠标的状态
    通过给事件处理程序传递this参数,获取事件源对象的引用。单机提交按钮时在信息框中显示用户输入的字符。
    在标签的事件属性字符串中编写程序,检查用户输入的密码明文
    通过使用浏览器对象模型,输出当前浏览器窗口中打开的文档的URL信息,并将显示在窗口中。
    创建一个卡片对象,卡片上标有“名字”、“地址”和“电话”等信息。名片对象提供一个方法以输出这些信息。
    测试Array对象的sort方法的作用。将1985,1970,1999,1998,2000,1963这些年份按升序输出。
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/15079795.html
Copyright © 2020-2023  润新知