zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 16

    http://codeforces.com/contest/710

    A:水题

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<int,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=300000+10,maxn=400000+10,inf=0x3f3f3f3f;
    
    
    int main()
    {
        fio;
        string s;
        cin>>s;
        if(s[0]=='a'||s[0]=='h')
        {
            if(s[1]=='8'||s[1]=='1')puts("3");
            else puts("5");
        }
        else
        {
            if(s[1]=='8'||s[1]=='1')puts("5");
            else puts("8");
        }
        return 0;
    }
    /********************
    
    ********************/
    A

    B:找规律题,当时一直以为是三分,所以一直wa。。。。

    题意:一堆数,找x使得x和他们的距离差的和最小

    首先不可能在两端,当x在两个点之间时,我们可以列出距离的公式,显然他是一元一次方程,肯定单调,所以在两端取,前后缀维护一下即可,但是我看别人都是第二种,虽然直觉上确实是对的,但是我也不会严格证明

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<int,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=300000+10,maxn=400000+10,inf=0x3f3f3f3f;
    
    ll a[N],sum1[N],sum2[N];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)sum1[i]=sum1[i-1]+a[i];
        for(int i=n;i>=1;i--)sum2[i]=sum2[i+1]+a[i];
        ll ans=1e18,id;
        for(int i=1;i<=n;i++)
        {
            ll te=sum2[i+1]-a[i]*(n-i)+a[i]*(i-1)-sum1[i-1];
            if(ans>te)
            {
                ans=te;
                id=a[i];
            }
        }
        printf("%lld
    ",id);
        return 0;
    }
    /********************
    
    ********************/
    B前缀和
    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<int,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=300000+10,maxn=400000+10,inf=0x3f3f3f3f;
    
    ll a[N],sum1[N],sum2[N];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        sort(a+1,a+1+n);
        printf("%lld
    ",a[(n+1)/2]);
        return 0;
    }
    /********************
    
    ********************/
    B找规律

    C:题意:给你一个奇数n,让你构造一个n*n矩阵要求填的数只能是1到n*n,每个格子不同,每行每列以及主副对角线和都必须是奇数

    解法:我们先1到n*n顺序填,填完之后会发现有一些圈是不满足的,比如

    1 2 3

    4 5 6

    7 8 9

     那么我们把最外圈平移一格变成了

    2 3 6

    1 5 9

    4 7 8,这样就能满足条件了,以此类推3,7,11....都需要旋转

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<int,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=100+10,maxn=400000+10,inf=0x3f3f3f3f;
    
    int ans[N][N];
    int main()
    {
        int n;
        scanf("%d",&n);
        int num=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                ans[i][j]=num++;
    
    //    for(int i=1;i<=n;i++)
    //    {
    //        for(int j=1;j<=n;j++)
    //            printf("%2d ",ans[i][j]);
    //        puts("");
    //    }
        for(int i=3;i<=n;i+=4)
        {
            int p=(n+i)/2;
            vector<int>v;
            for(int j=n-p+1;j<=p;j++)
                v.pb(ans[n-p+1][j]);
            for(int j=n-p+2;j<=p;j++)
                v.pb(ans[j][p]);
            for(int j=p-1;j>=n-p+1;j--)
                v.pb(ans[p][j]);
            for(int j=p-1;j>=n-p+2;j--)
                v.pb(ans[j][n-p+1]);
    //        for(int j=0;j<v.size();j++)
    //            printf("%d ",v[j]);
    //        puts("");
            int cnt=1;
            for(int j=n-p+1;j<=p;j++)
                ans[n-p+1][j]=v[cnt++];
            for(int j=n-p+2;j<=p;j++)
                ans[j][p]=v[cnt++];
            for(int j=p-1;j>=n-p+1;j--)
                ans[p][j]=v[cnt++];
            for(int j=p-1;j>=n-p+2;j--)
            {
                if(cnt==v.size())cnt=0;
                ans[j][n-p+1]=v[cnt++];
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                printf("%2d ",ans[i][j]);
            puts("");
        }
        return 0;
    }
    /********************
    1010101
    0101010
    1010101
    0101010
    1010101
    0101010
    1010101
    ********************/
    C

    D:给你a1,a2,b1,b2,l,r,要求找到在lr之间的x,满足x=a1*k+b1=a2*l+b2,kl必须大于0

    解法:扩展中国剩余定理,中国剩余定理是对于y=a1*x+b1=a2*x+b2

    变形一下a1*x+a2*y=b2-b1;,我们可以用扩展gcd来求这个方程的解,

    扩展gcd:对于直接套exgcd的x,y是a*x+b*y=gcd(a,b)的解,对于原方程的解我们需要乘上c/gcd(a,b),那么我们得到了一个特解x0,通解x=x0+k*b/gcd(a,b)

    那么我们再回到中国剩余定理上,将exgcd求得的通解x带入y=a1*x+b1,就得到了通解y,y%lcm(a1,a2)又等于0,

    对于这题来说,我们找到了通解y,但是需要在l,r之间,而且满足k,l为正数,当然我们可以将区间缩小到(max(l,max(b1,b2)),r)这样就满足了 kl>=0,我们将特解减到比l小的地方,然后加上r到y,减去l到y

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<ll,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=20000+10,maxn=90000+10,inf=0x3f3f3f3f;
    
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1,y=0;
            return a;
        }
        ll ans=exgcd(b,a%b,x,y);
        ll t=x;x=y;y=t-a/b*y;
        return ans;
    }
    int main()
    {
        ll a1,a2,b1,b2,l,r;
        scanf("%lld%lld%lld%lld%lld%lld",&a1,&b1,&a2,&b2,&l,&r);
        ll a=a1,b=a2,c=b2-b1,x,y;
        ll d=exgcd(a,b,x,y);
        ll lcm=a/__gcd(a,b)*b;
        l=max(l,max(b1,b2));
        x*=c/__gcd(a,b);
        if(c%d!=0||l>r)puts("0");
        else
        {
            ll t=b/__gcd(a,b);
            x=(x%t+t)%t;//min position solution
            x=a1*x%lcm+b1;
            x%=lcm;
            if(l>0)x-=lcm;
            else x+=(int)(l/lcm-2)*lcm;
            ll ans=0;
    //        printf("%lld
    ",ans);
    //        printf("%lld %lld %lld %lld %lld %lld
    ",x,l,r,lcm,l%lcm,r%lcm);
            if(x<=r)ans+=(r-x)/lcm;
            if(x<=l-1)ans-=(l-1-x)/lcm;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    /********************
    4 2963 394 577593 125523962 628140505
    ********************/
    D

    E:两种操作一种把a个数+1或者-1,代价是x,一种是把a翻倍,代价是y,求a个数从0到n的最小代价

    解法:dp【i】表示从i到n所需的最小代价,当i为奇数dp只能从dp【i-1】或者dp【(i+1)/2】转移过来,因为更大的i+x转移过来和i+1是一样的,当i是偶数,dp【i】只能是dp【i/2】或者dp【i-1】转移,

    转移方程if(i&1)  dp[i]=min(dp[i-1]+x, dp[(i +1) >> 1] + x + y);else dp[i]=min(dp[i/2]+y,dp[i-1]+x);

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<ll,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=20000000+10,maxn=90000+10,inf=0x3f3f3f3f;
    
    ll dp[N];
    int main()
    {
        ll n,x,y;
        scanf("%lld%lld%lld",&n,&x,&y);
        for(int i=1;i<=n;i++)
            if(i&1)
                dp[i]=min(dp[i-1]+x, dp[(i +1) >> 1] + x + y);
            else
                dp[i]=min(dp[i/2]+y,dp[i-1]+x);
        printf("%lld
    ",dp[n]);
        return 0;
    }
    /********************
    
    ********************/
    E

    F:题意:三种操作,一种加一个字符串到集合中(保证不相同)一种删除某个字符串(保证一定存在),一种查询集合中的字符串在给定的字符串中出现过几次(重合的也算)

    解法:考虑建多个ac自动机,但是建n个ac自动机肯定是会mle的,我们可以用二进制分组

    二进制分组:例如21=16+4+1,当21+1时,22=16+4+1+1=16+4+2保证每一位是2的幂次,那么对于每次加,最多只可能有logn个操作,查询也变成了logn个操作,这样就比直接暴力的n^2快多了,复杂度是nlognlogn

    现在我们建logn个ac自动机,每次加入时像二进制分组这样合并两个ac自动机,合并我们可以直接暴力删掉,然后暴力重新建图,建立fail指针

    对于删除操作我们考虑建两个ac自动机,然后一个代表+,一个代表-,最后的结果就是第一个-第二个

    然后这题的数据比较水,所有字符串hash也能过,附上字符串hash的代码(ac自动机的代码懒得写了= =)

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pii pair<int,int>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    
    const double g=10.0,eps=1e-12;
    const int N=300000+10,maxn=400000+10,inf=0x3f3f3f3f;
    const ll bs=199;
    
    map<int,set<int> >m;
    int Hash[N];
    int ha(char *s)
    {
        int ans=0,len=strlen(s);
        for(int i=0;i<len;i++)
            ans=(bs*ans+s[i])%mod;
        return ans;
    }
    int query(char *s)
    {
        int ans=0,v=ha(s),len=strlen(s);
        for(auto x=m.begin();x!=m.end()&&x->fi<=len;x++)
        {
            int res=0;
            for(register int i=0;i<x->fi;i++)
                res=(bs*res+s[i])%mod;
            if(x->se.find(res)!=x->se.end())ans++;
            for(int i=0;i+x->fi<len;i++)
            {
                res=(bs*res+s[i+x->fi]-(ll)Hash[x->fi]*s[i])%mod+mod;
                if(res>mod)res-=mod;
                if(x->se.find(res)!=x->se.end())ans++;
            }
        }
        return ans;
    }
    char s[N];
    int main()
    {
        int n;scanf("%d",&n);
        Hash[0]=1;
        for(int i=1;i<N;i++)
            Hash[i]=bs*Hash[i-1]%mod;
        for(register int i=0;i<n;i++)
        {
            int op;
            scanf("%d%s",&op,s);
            if(op==1)
            {
                int len=strlen(s);
                m[len].insert(ha(s));
            }
            else if(op==2)
            {
                int len=strlen(s);
                m[len].erase(ha(s));
                if(m[len].size()==0)m.erase(len);
            }
            else
            {
                printf("%d
    ",query(s));
                fflush(stdout);
            }
        }
        return 0;
    }
    /********************
    
    ********************/
    F
  • 相关阅读:
    初学Cocos2dx
    炸弹人NABCD分析
    求二维整数数组中最大联通子数组的和
    大道之简读书笔记1
    求首位相连二维数组最大子矩阵的和
    求首位相连一维数组最大子数组的和
    求二维数组最大子数组的和
    程序员修炼之道读后感3
    电梯调度需求分析
    课堂作业第四周课上作业二
  • 原文地址:https://www.cnblogs.com/acjiumeng/p/8308895.html
Copyright © 2011-2022 走看看