Codeforces Round #420 (Div. 2)

Posted notnight

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #420 (Div. 2)相关的知识,希望对你有一定的参考价值。

A - Okabe and Future Gadget Laboratory

水题,瞎暴力就能过。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[55][55];
bool judge(int x,int y,int v)
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==x || j==y) continue;
            if(a[x][j]+a[i][y]==v) return true;
        }
    }
    return false;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(a[i][j]!=1)
            {
                if(!judge(i,j,a[i][j]))
                {
                    puts("No");
                    return 0;
                }
            }
        }
    }
    puts("Yes");
    return 0;
}
View Code

B - Okabe and Banana Trees

也是比较水,枚举y轴,y轴上的数比较小,并且注意计算结果的时候不要枚举,用计算贡献的方法求和。

#include<bits/stdc++.h>
using namespace std;
long long m,b;
long long work(long long a,long long g)
{
    long long ans=0;
    ans+=a*(a+1)*(g+1)/2;
    ans+=g*(g+1)*(a+1)/2;
    return ans;
}
int main()
{
    cin>>m>>b;
    long long ans=0;
    for(long long i=0;i<=b*m;i++)
    {
        long long now=-i/m+b;
        if((now-b)*m==-i)
        {
            long long res=work(now,i);
            if(ans<work(now,i)) ans=res;
        }
    }
    cout<<ans<<endl;
    return 0;
}
View Code

C - Okabe and Boxes

题目大意: 有两种操作,一种往栈里面加数,一种从栈顶取数字,必须从小到大取出,并且取到这个数

的时候这个数一定在里面。如果当前的栈顶不是当前要取的数就进行一次排序,问你取完为止一共排序

了几次。

 

思路:因为题目已经说了一定不存在取这个数的时候这个数不再里面的情况,所以每当我们取的时候,如果

栈顶就是我们要的数就取走,如果不是我们就把栈清空说明栈里面的数已经排序好了,如果栈里面的元素是

空的说明可以直接取(因为这个数肯定在里面而且在最上面,因为比他小的数都取完了,而且栈里面已经排好序了)。

#include<bits/stdc++.h>
using namespace std;
const int N=3*1e5+5;
int n;
int sk[N],top;
int main()
{
    top=0;
    cin>>n;
    sk[++top]=0;
    int now=1;
    int ans=0,g;
    char s[5];
    for(int i=1;i<=2*n;i++)
    {
        scanf("%s",s);
        if(s[0]==\'a\')
        {
            scanf("%d",&g);
            sk[++top]=g;
        }
        else
        {
            //cout<<top<<" "<<sk[top]<<" "<<now<<endl;
            if(sk[top]==0)
            {
                now++;
                continue;
            }
            if(sk[top]==now)
            {
                top--,now++;
                //cout<<top<<endl;
            }
            else
            {
                now++;
                ans++;
                while(sk[top]!=0) top--;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

CodeForces - 821D

题目大意:有n*m个城市排成n行m列的矩形,相邻的城市可以互相通过。Okabe要从(1,1)走到(n,m),但是Okabe十分怕黑,

所以走过的城市必须是亮的才能通过。已知有k个城市是永远亮的,其他城市都是暗的。除此之外,Okabe可以在任何一个

永久都亮的城市发动魔法,让某一行或某一列暗的城市都暂时变亮。注意,两次魔法效果不能共存,也就是说,如果已经发

动了魔法,再要发动魔法的话,上一次发动的魔法效果就会先消失(非永久亮的城市变回暗的),然后再发动新的魔法效果

(所以如果站在暂时亮的城市发动新的魔法,就会GG)。问最少使用多少次魔法,能够使Okabe从(1,1)走到(n,m)。不能走到输出-1。

 

 

思路:哎,好蓝啊,写不来,要把这个转化为一个最短路问题,难点就是怎么转换。

把永久都亮的城市,和n行、m列都看成点,共n+m+k个点。那么连边只有三种关系。

  • 点到点,永久亮的点,到四个方向永久亮的点,花费是0。
  • 点到线(一行或一列),永久亮的点到和3个行、3个列,花费是1。
  • 线到点,行或列到与其挨着的永久亮的点,花费是0。

需要注意的是,(n,m)点不是亮的,我们需要再加一个编号为k+1的点表示点(n,m),并且

将代表n行的这个点和代表m列的这个点向编号为k+1的点连边(因为只有这两个点能到达(n,m))。

然后我们就只要跑一次Dijkstra就可以了。

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e5+5;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
struct edge
{
    int to,cost;
    edge(){}
    edge(int _to,int _cost){to=_to,cost=_cost;}
};
vector<edge> e[N];
int n,m,k,d[N];//d[i] 表示从 起点到 i 的最短路。
map<pii,int> mp;//点对应点的编号。
pii p[N];// 记录点。
void Dj(int s)
{
    priority_queue<pii,vector<pii>,greater<pii> > Q;
    Q.push(mk(0,s));
    d[s]=0;
    while(!Q.empty())
    {
        pii now=Q.top();Q.pop();
        if(d[now.se]<now.fi) continue;
        for(int i=0;i<e[now.se].size();i++)
        {
            edge p=e[now.se][i];
            if(d[p.to]>d[now.se]+p.cost)
            {
                d[p.to]=d[now.se]+p.cost;
                Q.push(mk(d[p.to],p.to));
            }
        }
    }
}
int main()
{
    memset(d,inf,sizeof(d));
    cin>>n>>m>>k;
    int st=1e4+5;
    mp.clear();
    for(int i=1;i<=k;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        e[i].pb(edge(x+st,1));
        e[i].pb(edge(x+st+1,1));
        e[i].pb(edge(x+st-1,1));
        e[i].pb(edge(y+2*st,1));
        e[i].pb(edge(y+2*st-1,1));
        e[i].pb(edge(y+2*st+1,1));//将与点当前的和相邻的行和列与点连边。

        e[x+st].pb(edge(i,0));
        e[x+st+1].pb(edge(i,0));
        e[x+st-1].pb(edge(i,0));
        e[y+2*st+1].pb(edge(i,0));
        e[y+2*st-1].pb(edge(i,0));
        e[y+2*st].pb(edge(i,0));//反向连边。
        mp[mk(x,y)]=i;//记录点和它的编号。
        p[i]=mk(x,y);
    }
    //点和相邻的点连边。
    for(int i=1;i<=k;i++)
    {
        for(int j=0;j<4;j++)
        {
            int nx=p[i].fi+dx[j];
            int ny=p[i].se+dy[j];
            if(mp[mk(nx,ny)]) e[i].pb(edge(mp[mk(nx,ny)],0));
        }
    }
    //看终点是不是亮着的。
    if(!mp[mk(n,m)])
    {
        e[n+st].pb(edge(k+1,0));
        e[m+2*st].pb(edge(k+1,0));
    }
    Dj(mp[mk(1,1)]);
    int ans=inf;
    if(!mp[mk(n,m)]) ans=d[k+1];
    else ans=d[k];
    if(ans==inf) ans=-1;
    cout<<ans<<endl;
    return 0;
}
View Code

 

E - Okabe and El Psy Kongroo

题目大意: 给你n条平行于x轴的线段,线段的y值位于(0到15)之间,且前一条的r 和 后一条的 l 相等

即,前一条和后一条映射到x轴上是刚好接着的。你刚开始在(0,0),你有三种走法,从( i , j )->( i + 1, j )

( i , j )->( i + 1 , j + 1)  ,( i , j )->( i + 1 , j - 1 ) , 走的时候不能超过线的上端。  k<1e18;

问你从(0,0)开始到(k,0)一共有多少种走法。

 

 

思路: 状态转移方程我们能非常快地写出来,再是如果随着x轴一次一次地更新复杂度太高,因为k有1e18

的大小,这样我们就能考虑用矩阵快速幂优化dp,终点就是找到一个满足状态转移方程的举证,因为y是0 到 15

所以我们能找到这样一个16*16的矩阵:

 

 a [ i ]代表当前 x 的为止 y 值为 i 的方案数。这样就能解决这个问题了,代码中细节需要注意。

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=16;
int n;
ll k;
struct Matrix
{
    ll m[N][N];
}A,B,p,st;
int init()
{
    memset(A.m,0,sizeof(A.m));
    A.m[0][0]=A.m[0][1]=A.m[15][15]=A.m[15][14]=1;
    for(int i=1;i<15;i++) A.m[i][i]=A.m[i][i-1]=A.m[i][i+1]=1;
    memset(p.m,0,sizeof(p.m));
    p.m[0][0]=1;
    memset(st.m,0,sizeof(st.m));
    for(int i=0;i<N;i++) st.m[i][i]=1;
}
Matrix Matrix_mul(Matrix a,Matrix b,int len)
{
    Matrix ret;
    memset(ret.m,0,sizeof(ret.m));
    for(int k=0;k<=len;k++)
    {
        for(int i=0;i<=len;i++)
        {
            if(a.m[i][k])
            {
                for(int j=0;j<=len;j++) ret.m[i][j]=(ret.m[i][j]+(ll)a.m[i][k]*b.m[k][j])%mod;
            }
        }
    }
    return ret;
}
Matrix Matrix_q_pow(Matrix a,ll k,int len)
{
    Matrix ret=st;
    while(k)
    {
        if(k&1)
        {
            ret=Matrix_mul(a,ret,len);
        }
        a=Matrix_mul(a,a,len);
        k>>=1;
    }
    return ret;
}
int main()
{
    init();
    cin>>n>>k;
    ll a,b;
    int c;
    bool flag=false;
    for(int i=1;i<=n;i++)
    {
        scanf("%I64d%I64d%d",&a,&b,&c);
        if(b>k)
        {
            b=k;
            flag=true;
        }
        B=Matrix_q_pow(A,b-a,c);
        for(int j=c+1;j<N;j++) p.m[j][0]=0;
        B=Matrix_mul(B,p,c);
        for(int j=0;j<=c;j++) p.m[j][0]=B.m[j][0];
        if(flag) break;
    }
    cout<<B.m[0][0]<<endl;
    return 0;
}
View Code

 

以上是关于Codeforces Round #420 (Div. 2)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #420 (Div. 2)

Codeforces Round #420 E

Codeforces Round #420 B

Codeforces Round #420 C

Codeforces Round #420 (Div. 2) A-E

Codeforces Round #420 A题翻译(17.6.25)