• Educational Codeforces Round 78 (Rated for Div. 2)


    A. Shuffle Hashing (CF 1278 A)

    题目大意

    给定两个字符串(a,b),现改变(a)串中的字母顺序,问是否存在某种顺序在(b)串中出现。

    解题思路

    先对(a)进行排序,由于字符串长度最多只有(100),我们就枚举(b)串中的起始位置,然后把长度为(a.size())的子串截取下来排序看看是否和(a)相等即可。复杂度(O(n^2logn))
    ((CF)上的(dalao)的思维好快(qwq))

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    
    bool check(void) {
        string a,b;
        cin>>a>>b;
        sort(a.begin(),a.end());
        int la=a.size();
        int lb=b.size();
        for(int i=0;i<lb;++i){
            if (i+la>lb) break;
            string c(b,i,la);
            sort(c.begin(),c.end());
            if (a==c) return true;
        }
        return false;
    }
    
    int main(void) {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        int kase; 
        cin>>kase;
        while(kase--) if (check()) printf("YES
    "); else printf("NO
    ");
        return 0;
    }
    


    B. A and B (CF 1278 B)

    题目大意

    给定两个数(a,b),第一次选(a)(b)(1),第二次选(a)(b)(2),第三次选(a)(b)(3),依次类推,问最少多少次操作使得(a,b)相等。

    解题思路

    (dis=abs(a-b)),我们进行了(n)次操作,这(n)次操作中有若干次是使(dis)变大,有若干次是使(dis)变小,最终应有(dis+x-y=0),其中(x+y=dfrac {nleft( n+1 ight) }{2}),联立这两个式子消去(y)(dis+2*x=dfrac {nleft( n+1 ight) }{2})
    这是个关于(n)的二次函数,其中(n)是单调递增的,那么我们需要找到最小的非负整数(x),使得(n)是非负整数即可。
    解这个一元二次方程即可得到

    [n=dfrac {-1+sqrt {1+16x+8dis}}{2} (另解小于0舍去) ]

    我们对(x)(0)开始枚举找到第一个(1+16x+8dis)是某奇数的平方即可(代码被注释的那段)
    然后我看了下(CF)上的前排(dalao)们发现他们写的思路清奇且一模一样(0.0)
    由于(x)是非负数所以有(dfrac {nleft( n+1 ight) }{2}geq dis),然后我们对等式(dis+2*x=dfrac {nleft( n+1 ight) }{2})两边对(2)取模,即可得到(dis%2=dfrac {nleft( n+1 ight) }{2} %2),所以我们对(n)(1)开始,找到第一个(n)使得(dfrac {nleft( n+1 ight) }{2} geq dis)(dis)(dfrac {nleft( n+1 ight) }{2})的奇偶性相同,那么就一定存在一个非负整数(x)使得(dis+2*x=dfrac {nleft( n+1 ight) }{2})
    (()这就是大佬(qwq))

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    /* long long a,b,dis,qwq;
    
    bool check(long long x){
        long long qaq=sqrt(x);
        if (qaq*qaq==x) {
            if (qaq&1) return true;
        }
        else return false;
    }
    
    void Input(void) {
        read(a);
        read(b);
        //if (dis==0) {printf("0
    "); return;}
        dis=8ll*ABS(a-b)+1ll;
        for(int i=0;1;++i){
            if (check(dis+16*i)) {qwq=sqrt(dis+16*i); break;}
        }
        printf("%lld
    ",((qwq-1ll)/2ll));
    } */
    
    void Input(void){
        long long a,b;
        read(a); read(b);
        long long dis=ABS(a-b);
        long long sum=0,cnt=0;
        while(sum<dis||((sum&1)!=(dis&1)))
            sum+=++cnt;
        printf("%lld
    ",cnt);
    }
    
    void Solve(void) {}
    
    void Output(void) {}
    
    int main(void) {
        //ios::sync_with_stdio(false);
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            //printf("Case #%d: ", i);
            Input();
            Solve();
            Output();
        }
        return 0;
    }
    


    C. Berry Jam (CF 1278 C)

    题目大意

    (2n)个数字,每个数字是(1)或者(2),现在从中间开始,每次往左或往右删去最近的一个数,现需要使(1)(2)的个数相等,求最小删除数的个数。

    解题思路

    我们枚举往左删除的端点,看看右端点最短要延伸到哪里。假设左端点位置为(i),除去([i,n])(1)(2)的个数后,(1)(2)的差值为(dis),我们需要知道最小的右端点(r),使得区间([n+1,r])(1)(2)的差值也为(dis),这样我们除去([n+1,r])中的(1)(2)后,剩余的(1)(2)的个数就相等了。而最小的(r)我们可以用(unordered\_map)来维护,记录右半部分(1)(2)的差值为(d)的最小位置为(map[d]),那此时删除的个数就是((n-i+1)+map[d]-n)。这样复杂度就是(O(n))了。
    其实由于(n)最多(1e5),我们开个(4e5)的数组来记录位置也是可以的。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    unordered_map<int,int> qwq;
    
    int n,dis,ans;
    
    const int N=1e5+8;
    
    int a[N],sum[3];
    
    void Input(void) {
        qwq.clear();
        read(n);
        sum[1]=sum[2]=0;
        for(int i=1;i<=n;++i) {
            read(a[i]);
            ++sum[a[i]];
        }
        int s[3];
        s[1]=s[2]=0;
        for(int u,i=1;i<=n;++i){
            read(u);
            ++s[u];
            if (qwq[s[1]-s[2]]==0) qwq[s[1]-s[2]]=i;
        }
        sum[1]+=s[1];
        sum[2]+=s[2];
    }
    
    void Solve(void) {
        dis=sum[1]-sum[2];
        if (dis==0) ans=0;
        else{
            if (qwq[dis]) ans=qwq[dis];
            else ans=2147483647;
            for(int i=n;i>=1;--i){
                if (a[i]==1) --dis;
                else ++dis;
                if (dis==0) ans=MIN(ans,n-i+1);
                else if (qwq[dis]) ans=MIN(ans,n-i+1+qwq[dis]);
            }
        }
        printf("%d
    ",ans);
    }
    
    void Output(void) {}
    
    int main(void) {
        //ios::sync_with_stdio(false);
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            //printf("Case #%d: ", i);
            Input();
            Solve();
            Output();
        }
        return 0;
    }
    


    D. Segment Tree (CF 1278 D)

    题目大意

    真·线段树
    给定n条线段,端点值互不相同,如果两条线段有交叉部分(包含不算),则这两条线段之间连一条边。问最终这些线段形成的图是不是棵树。

    解题思路

    注意到线段右端点(1leq l<rleq 2n),这就意味着所有线段的两个端点的取值都在([1,2n])中。
    我们先对线段的左端点进行排序,依次考虑每个线段的与哪些线段有连边。
    当前考虑的是第(i)条线段,由于连边条件具有对称性,我们可以只考虑第(1)条到第(i-1)条线段与第(i)条线段。
    由于前面的线段的左端点(l)均小于(l_i),那么我们只要找到其右端点大于(l_i)且小于(r_i)的线段,它们会有连边。而我们要找的右端点是个连续的范围,那么我们可以把(1)(i-1)的线段的右端点丢到(set)里面,这样我们就可以在(O(logn))的时间内找到对应的线段。
    而当连的边数大于等于(n)时,此时不可能是棵树,直接(break)了。最后再判断边数是否是(n-1)并再(DFS)遍历一遍看看是不是一个连通块即可。复杂度(O(nlogn))

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=5e5+8;
    
    set<pair<int,int>> qwq;
    
    vector<pair<int,int>> line;
    
    vector<int> edge[N];
    
    vector<bool> sign;
    
    int n;
    
    void DFS(int x){
        sign[x]=true;
        for(auto i:edge[x]) if (!sign[i]) DFS(i);
    }
    
    bool Solve(void) {
        read(n);
        for(int u,v,i=1;i<=n;++i){
            read(u);
            read(v);
            line.push_back(make_pair(u,v));
        }
        sort(line.begin(),line.end());
        int cnt=0;
        for(int i=0;i<n;++i){
            auto it=qwq.lower_bound(make_pair(line[i].first,0));
            while(it!=qwq.end()&&(*it).first<=line[i].second){
                ++cnt;
                if (cnt>=n) return false;
                edge[i].push_back((*it).second);
                edge[(*it).second].push_back(i);
                ++it;
            }
            qwq.insert(make_pair(line[i].second,i));
        }
        if (cnt<n-1) return false;
        sign.resize(n);
        DFS(0);
        for(auto i:sign)
            if (i==false) return false;
        return true;
    }
    
    int main(void) {
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        if (Solve()) printf("YES
    "); else printf("NO
    ");
        return 0;
    }
    


    E. Tests for problem D (CF 1278 E)

    题目大意

    上一题的逆向,给定一棵带标号的树,要求构造一组线段使之按照上题连线的规则,形成给定的树。输出每个标号对应的线段的左右端点。端点唯一且小于等于(2n).

    解题思路

    我们考虑第一个线段,(l_1=1),而因为第一个线段与(k_1)个线段有连线,故(r_1-l_1-1=k_1),即第一个线段之间的点用来存放与第一条线段相连的左端点。
    然后我们考虑与第一条线段相连的线段,稍加分析可以知道从第一条线段右端点往左开始考虑会比较方便。
    则对于第二条线段,它的(l_2=r_1-1),除去第一条线段,第二条线段与(k_2)条线段相连,那么(r_2-l_1-1=k_2)。其他的依次类推,最后我们可以形成一张

    看似比较复杂的构造方法,其实用(DFS)即可很容易的实现。
    我们依次为与一个线段相连的线段的左端点安排位置,安排完后下一个位置就是该线段右端点的位置,然后倒序遍历与这个线段相连的线段,重复同样操作即可。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=5e5+8;
    
    vector<int> edge[N];
    
    int n,k;
    
    int l[N],r[N];
    
    void DFS(int u,int fa){
        for(auto v:edge[u])
            if (v!=fa) l[v]=++k;
        r[u]=++k;
        reverse(edge[u].begin(),edge[u].end());
        for(auto v:edge[u])
            if (v!=fa) DFS(v,u);
    }
    
    int main(void) {
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        read(n);
        for(int u,v,i=1;i<=n;++i){
            read(u);
            read(v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        k=0;
        l[1]=++k;
        DFS(1,1);
        for(int i=1;i<=n;++i) printf("%d %d
    ",l[i],r[i]);
        return 0;
    }
    


    F. Cards (CF 1278 F)

    题目大意

    给定(n,m,k),有(m)张牌,其中一张为鬼牌。现进行(n)次操作,每次操作,对(m)张拍进行洗牌,从上到下,拿第一张牌,看牌,放牌。记拿的第一张牌是鬼牌的次数为(x),问(x^k)的期望值。

    解题思路

    根据期望的定义我们可以很容易知道答案就是:

    [sumlimits_{i=0}^{n}C_{n}^{i} imes (dfrac{1}{m})^{i} imes (1-dfrac{1}{m})^{n-i} imes i^k ]

    但直接算会超时,我们得另想办法。

    经过观察我们发现这个式子和二项式展开式((dfrac{x}{m}+1-dfrac{1}{m})^n=sumlimits_{i=0}^{n}C_{n}^{i} imes (dfrac{1}{m})^{i} imes (1-dfrac{1}{m})^{n-i} imes x^i)十分相像,差别就是(i^k)

    我们可以先对这个式子两边求一阶导,这样右边就变成了(sumlimits_{i=0}^{n}C_{n}^{i} imes (dfrac{1}{m})^{i} imes (1-dfrac{1}{m})^{n-i} imes i imes x^{i-1}),我们对等式两边再乘以(x),再求一次导,重复(k)次,我们右边就有(i^k),此时取(x=1),右边就是我们想要的式子了,现在考虑左边该如何计算。

    由于(m)是常数,我们把(m)扔到一边,考虑((x+m-1)^n)在求导以及乘以(x)下的变化。

    我们可以发现得到的因子都是形如(x^i imes (x+m-1)^{n-i}),那么我们设(A_i=x^i imes (x+m-1)^{n-i}),它的系数为(B_{ij})(j)表示求了(j)次导。那么(B_{ij}A_i)求导再乘以(x)后产生了两项,一项是(i imes B_{ij} imes A_{i})贡献给了(B_{i(j+1)} imes A_{i}),另一项((n-i) imes B_{ij} imes A_{i+1})贡献给了(B_{(i+1)(j+1)} imes A_{i+1})

    因此我们可以(O(k^2))计算出求导了(k)次后,各个(A_i)的系数,代入(x=1),计算结果,最后再除以(m^n)即可得到最终答案。

    注意当(n<k)的时候,(A_n)求导后的贡献没有(A_{n+1})的项。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const LL mo=998244353;
    
    LL qpower(LL a,LL b){
        LL qwq=1;
        while(b){
            if (b&1) qwq=qwq*a%mo;
            a=a*a%mo;
            b>>=1;
        }
        return qwq;
    }
    
    LL inv(LL x){
        return qpower(x,mo-2);
    }
    
    int main(void) {
        LL n,m,k;
        read(n);
        read(m);
        read(k);
        LL a[k+8]={0};
        a[0]=1;
        for(int i=1;i<=k;++i){
            for(int j=i-1;j>=0;--j){
                if (j<n) a[j+1]=(a[j+1]+a[j]*(LL)(n-j)%mo)%mo;
                a[j]=a[j]*(LL)j%mo;
            }
        }
        LL qwq=qpower(m,max(n-k,0ll));
        LL ans=0;
        for(int i=min(n,k);i>=0;--i){
            ans=(ans+qwq*a[i])%mo;
            qwq=qwq*m%mo;
        }
        ans=ans*inv(qpower(m,n))%mo;
        write(ans,'
    ');
        return 0;
    }
    
    


  • 相关阅读:
    oracle解决连接池不足
    ORA-12537:TNS连接已关闭
    oracle 11g 大量废连接占满数据库连接问题处理
    oracle: 浅谈sqlnet.ora文件的作用,及SQLNET.AUTHENTICATION_SERVICES设置
    查询oracle数据库的数据库名、实例名、ORACLE_SID
    工程:有价值的事物的创建过程,及依赖的资源与知识
    工程学
    并发的本质是任务空间与执行空间
    异步的本质是不确定性
    聊一聊 redux 异步流之 redux-saga
  • 原文地址:https://www.cnblogs.com/Lanly/p/12074845.html
Copyright © 2020-2023  润新知