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\n",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\n",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\n",ans);
//        printf("%lld %lld %lld %lld %lld %lld\n",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\n",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\n",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\n",query(s));
            fflush(stdout);
        }
    }
    return 0;
}
/********************

********************/
F

 

posted @ 2018-01-18 10:27  walfy  阅读(199)  评论(0编辑  收藏  举报