AtCoder Grand Contest 015

AtCoder Grand Contest 015

A - A+...+B Problem

有一个人有\(n\)个数,最小的是\(A\),最大的是\(B\),其他数位置,问一共有多少种和的可能情况。

显然能够取到的是一段连续值,那么求出最小值和最大值就行了。

#include<iostream>
using namespace std;
long long n,a,b,l,r;
int main()
{
    cin>>n>>a>>b;
    if(a>b){puts("0");return 0;}
    l=b+(n-1)*a,r=a+(n-1)*b;
    cout<<max(0ll,r-l+1)<<endl;
    return 0;
}

B - Evilator

有一个电梯,有\(n\)层楼,给定了一个字符串\(S\),\(S_i\)告诉你了这个电梯在这一层只能向上走或者是向下走。

现在问你任意两层之间需要坐几次电梯,求出所有情况的和。

显然答案不是\(1\)就是\(2\),那么对于每个位置判断一下有多少个是\(1\)有多少个是\(2\)就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;long long ans;
char S[100010];
int main()
{
    scanf("%s",S+1);n=strlen(S+1);
    for(int i=1;i<=n;++i)
        if(S[i]=='U')ans+=(n-i)+2*(i-1);
        else ans+=2*(n-i)+(i-1);
    printf("%lld\n",ans);
    return 0;
}

C - Nuske vs Phantom Thnook

有一个网格图,一些格子被涂蓝了,保证蓝色的格子构成了一棵树。

每次询问问你一个子矩形内蓝色格子构成的联通块数量。

既然是一棵树,那么联通块个数等于点数减去边数,然后就很好做了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 2010
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,m,Q;
char g[MAX][MAX];
int s[MAX][MAX],sl[MAX][MAX],su[MAX][MAX];
int Calc(int s[][MAX],int x1,int y1,int x2,int y2)
{
    if(x1>x2||y1>y2)return 0;
    return s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
}
int main()
{
    n=read();m=read();Q=read();
    for(int i=1;i<=n;++i)scanf("%s",g[i]+1);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            s[i][j]=(g[i][j]=='1');
            sl[i][j]=(g[i][j]=='1'&&g[i][j-1]=='1');
            su[i][j]=(g[i][j]=='1'&&g[i-1][j]=='1');
        }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
            sl[i][j]+=sl[i-1][j]+sl[i][j-1]-sl[i-1][j-1];
            su[i][j]+=su[i-1][j]+su[i][j-1]-su[i-1][j-1];
        }
    while(Q--)
    {
        int x1=read(),y1=read(),x2=read(),y2=read();
        printf("%d\n",Calc(s,x1,y1,x2,y2)-Calc(sl,x1,y1+1,x2,y2)-Calc(su,x1+1,y1,x2,y2));
    }
    return 0;
}

D - A or...or B Problem

由\([A,B]\)之间的数构成的非空集合的\(or\)值有多少种。

感觉很有想法,然而就是不会做,。。。

首先\(A=B\)不用管了,只考虑\(A=B\) 的情况,那么先找到二进制位下\(A,B\)不同的第一位,前面的部分可以直接丢掉,因为对于答案不产生影响。

假设最高位是\(k\),那么我们把集合分成两个部分,一半是\([A,2^k)\),另一半是\([2^k,B]\),在第一部分里面任意选数只能选出\([A,2^k)\)的值,在第二部分中选数,考虑\(B\)的次大二进制为\(l\),那么可以选的范围在\([2^k,2^k+2^l)\)之间,如果两个部分同时选的话,那么可以得到的范围是\([2^k+A,2^{k+1})\)。

这三个部分取并就是答案了。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
ll A,B,ans;
int main()
{
    cin>>A>>B;
    if(A==B){puts("1");return 0;}
    int k=60;
    for(;~k;--k)
    {
        if((A>>k&1)^(B>>k&1))break;
        if(A>>k&1)A^=1ll<<k,B^=1ll<<k;
    }
    int l=k-1;
    while((~l)&&!(B&(1ll<<l)))--l;
    ll L=(!~l)?B:((1ll<<k)+(1ll<<(l+1))-1);
    ll R=(1ll<<k)+A;
    ans=(1ll<<(k+1))-A;
    if(L+1<R)ans-=R-L-1;
    printf("%lld\n",ans);
    return 0;
}

E - Mr.Aoki Incubator

数轴上有\(n\)个人,第\(i\)个人一开始在\(X_i\)位置,用\(v_i\)的初速度往正方向移动。一开始有若干个人被染色,被染色的人如果碰到了未被染色的人,就把会未被染色的人给染色。

问在所有的\(2^n\)种初始的染色方案中,有多少种染色方法在足够长的时间之后能够让所有人都被染色。

首先最终的序列一定是唯一的,即所有人都按照速度的顺序依次排开,并且所有人相遇的时间也是固定的,那么唯一需要考虑的就只有染色的情况。

考虑现在只有一个点被染色,考虑哪些点会被染色。显然是位置在它左侧其速度比它快的,以及位置在它右侧并且速度比它慢的点会被追上。

把所有点按照速度从大往小排序,那么,对于一个点,在速度比它大的点中找到最靠右的那个,记做\(L\)。在速度比它小的点中找到最靠左的那个,记做\(R\) 。那么\([L,R]\)都会被染色(看起来挺容易证明的)。

于是问题等价于有\(n\)个区间,选出若干个使得他们的并恰好是全集的方案数。

那么这个东西直接\(dp\)就行了。

把所有区间按照右端点排序,设\(f[i]\)表示考虑了前\(i\)个区间,且\([1,R_i]\)都已经被覆盖的方案数。

这个东西可以用前缀和很方便的进行转移。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 1000000007
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Node{int x,v;}p[MAX];
int S[MAX],top;
bool operator<(Node a,Node b){return a.v<b.v;}
struct Line{int l,r;}a[MAX];
bool operator<(Line a,Line b){if(a.r!=b.r)return a.r<b.r;return a.l<b.l;}
int f[MAX],s[MAX],ans,n;
int main()
{
    n=read();
    for(int i=1;i<=n;++i)p[i].x=read(),p[i].v=read();
    sort(&p[1],&p[n+1]);
    for(int i=1;i<=n;++i)
    {
        if(!top||p[i].x>p[S[top]].x)S[++top]=i;
        int l=1,r=top,ret=i;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(p[S[mid]].x>=p[i].x)r=mid-1,ret=mid;
            else l=mid+1;
        }
        a[i].l=S[ret];
    }
    top=0;
    for(int i=n;i>=1;--i)
    {
        if(!top||p[i].x<p[S[top]].x)S[++top]=i;
        int l=1,r=top,ret=i;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(p[S[mid]].x<=p[i].x)r=mid-1,ret=mid;
            else l=mid+1;
        }
        a[i].r=S[ret];
    }
    sort(&a[1],&a[n+1]);
    for(int i=1,p=1;i<=n;++i)
    {
        while(a[p].r<a[i].l-1)++p;
        f[i]=(s[i-1]-s[p-1]+MOD)%MOD;
        if(a[i].l==1)f[i]=(f[i]+1)%MOD;
        if(a[i].r==n)ans=(ans+f[i])%MOD;
        s[i]=(s[i-1]+f[i])%MOD;
    }
    printf("%d\n",ans);
    return 0;
}

F - Kenus the Ancient Greek

给你\(n,m\),求\(x\in [1,n],y\in[1,m]\)中,让\(x,y\)做辗转相除的最大递归次数是多少。

并求出能够取到最大值的\((x,y)\)的对数。

首先最大值很容易算,不难发现就是斐波那契数列最大的相邻的两项,满足都在范围内。

考虑怎么统计答案,我们不妨令\(x\ge y\)。假设最大值是\(k\),递归次数是\(g(x,y)\),\(f_i\)为斐波那契数列第\(i\)项,且\(f_0=f_1=1\)。 那么\(g(x,y)\)的最大值为满足\(f_{k+1}\le x,f_{k}\le y\)的最大\(k\)。

剩下的部分戳这里吧

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
#define MOD 1000000007
#define pi pair<ll,ll>
#define mp make_pair
inline ll read()
{
    ll x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
ll f[110];
vector<pi> a[110];
int main()
{
    int T=read();
    f[0]=f[1]=1;
    for(int i=2;i<=100;++i)f[i]=f[i-1]+f[i-2];
    a[1].push_back(mp(1,2));a[1].push_back(mp(1,3));a[1].push_back(mp(1,4));
    for(int i=1;i<=100;++i)
        for(int j=0,l=a[i].size();j<l;++j)
        {
            ll x=a[i][j].second,y=a[i][j].first+x;
            while(y<=f[i+3]+f[i])a[i+1].push_back(mp(x,y)),y+=x;
        }
    while(T--)
    {
        ll n=read(),m=read();if(n>m)swap(n,m);
        if(n==1&&m==1){puts("1 1");continue;}
        int p,ans=0;for(int i=1;;++i)if(!(n>=f[i]&&m>=f[i+1])){p=i-1;break;}
        printf("%d ",p);if(p==1){printf("%lld\n",(n%MOD)*(m%MOD)%MOD);continue;}
        for(int j=0,l=a[p-1].size();j<l;++j)
        {
            ll x=a[p-1][j].first,y=a[p-1][j].second;
            if(y<=n)ans=(ans+(m-x)/y)%MOD;
            if(y<=m)ans=(ans+(n-x)/y)%MOD;
        }
        printf("%d\n",ans);
    }
}
上一篇:[Linux] 015 用户管理命令


下一篇:015_内置函数2