CodeForces Good Bye 2016 :C New Year and Rating、D New Year and Fireworks、E New Year and Old Subsequ

                                                      C. New Year and Rating

 很有意思的题啊,一个贪心思路却被我自己的二分思想缠绕住了,可是二分却发现不管二分初始分数还是结束分数都没有单调性,初始分数和最终分数都是在一个范围内的值,昨天看题解了不是很懂,今天过来直接A了。

题意:已知某大牛2016年共打了n场CF,规定分数大于等于1900的算div1,否者算div2。给出打每场比赛之前的division,以及这场比赛的rating变化值,未给出起始分数,求最终得分的最大值是多少,如果不存在输出Impossible,如果能无限大,输出Infinity。

思路:假设初始分数是pre,我们已知每场比赛前的div,我们用一个sum表示前i-1场分数变化的前缀和,如果这一场之前处于div1,则pre+sum>=1900,即pre>=1900-sum,如果处于div2,则应满足pre+sum<=1899,即pre<=1899-sum。我们设计一个上下界来表示pre的可行范围每次更新即可。

注意下界应设置成-INF,上界INF。

struct node
{
    int c,d;
} a[N];
int n;
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1; i<=n; i++) scanf("%d%d",&a[i].c,&a[i].d);
        int low=-INF,hig=INF,sum=0; //low<=pre_x<=hig
        for(int i=1; i<=n; i++)
        {
            if(a[i].d==1) low=max(low,1900-sum);
            else hig=min(hig,1899-sum);
            sum+=a[i].c;
        }
        if(low>hig)  puts("Impossible");
        else
        {
            if(hig>=INF) puts("Infinity");
            else printf("%d\n",hig+sum);
        }
    }
    return 0;
}


                                             D. New Year and Fireworks

 刚开始看感觉2^30次方的数据,怎么都会爆,毫无思路,队友提示了一下虽然总数量能达到这么多,但每个烟花的行走轨迹最坏的情况是左45右45度走不超过5步,,这样最大半径也就150。那么这个题的突破口就在这了。

啊,对偶,还没说题意:过年大家都喜欢放烟火,烟火每次爆炸会分裂成两个子烟火,分别朝着左45度和右45度前进不超过5步,然后再次爆炸。假设烟火处于一个无限大的方格上(每个格子都是1*1),给你n,表示最初的那个烟火总共会爆炸n次然后消失,然后n个不超过5的数,表示每次爆炸分裂的子烟火所走的步数,求这个平面有多少个方格被经过了。

上面提到我们总共才300*300的方格有效,那么即使有这么多子烟火会产生,但很多都是重复经过的路径,所以对答案没什么贡献,我们只需将这类烟火标记即可。答案最大300*300,爆搜即可。

int d[9][2]= {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};
bool vis[301][301][31][8][6],w[301][301];
int n,t[N],ans;
void dfs(int x,int y,int i,int dir,int dep)
{
    if(i>n||vis[x][y][i][dir][dep]) return ;
    if(!w[x][y]) ans++;
    w[x][y]=1;
    vis[x][y][i][dir][dep]=1;
    if(dep<t[i]) dfs(x+d[dir][0],y+d[dir][1],i,dir,dep+1);
    else
    {
        dfs(x,y,i+1,(dir-1+8)%8,0);
        dfs(x,y,i+1,(dir+1)%8,0);
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0; i<n; i++) scanf("%d",&t[i]);
        ans=0;
        dfs(150,150,0,0,1);
        printf("%d\n",ans);
    }
    return 0;
}


                                              E. New Year and Old Subsequence

 这个题真的很厉害,毕竟现场我神tourist都没做出来。

看了题解表示对那个矩阵转移还不是很明白,不过思路确实很强,从未接触过的新技能。

矩阵建模的思想结合线段树,每个节点都是储存了一个矩阵。

题意:给你n个字符的字符串,Q次查询,每次查询一个区间[l,r],问这个区间最少删除多少字符使得不能按顺序得到2016,并且能按顺序得到2017,如果不存在解输出-1。

题解大致:用4个种转态表示2017。

0:仅有2

1:含有20

2:含有201

3:含有2017

4:含有2016

那么i->i,我们已经有存在,所以要删除,所以a[i][i]=1?表示不太明白;

但i->i+1:不存在,不用删除,所以a[i][i+1]=0,这里还是很好理解。

然后重点在线段树合并,所以这个转移不是很好理解。

先占坑,以后回来研究。

const int N=2e5+10;
int n,q;
char s[N];
struct martix
{
    int b[5][5];
    friend martix operator +(martix A,martix B)
    {
        martix tmp;
        for(int i=0; i<5; i++)
            for(int j=0; j<5; j++)
            {
                tmp.b[i][j]=n;
                for(int k=0; k<5; k++)
                    tmp.b[i][j]=min(tmp.b[i][j],A.b[i][k]+B.b[k][j]);
            }
        return tmp;
    }
} a[N<<2];
void build(int l,int r,int k)
{
    if(l==r)
    {
        for(int i=0; i<5; i++)
            for(int j=0; j<5; j++) a[k].b[i][j]=i==j?0:n;
        if(s[l]=='2') a[k].b[0][0]=1, a[k].b[0][1]=0;
        if(s[l]=='0') a[k].b[1][1]=1,a[k].b[1][2]=0;
        if(s[l]=='1') a[k].b[2][2]=1,a[k].b[2][3]=0;
        if(s[l]=='7') a[k].b[3][3]=1, a[k].b[3][4]=0;
        if(s[l]=='6') a[k].b[3][3]=a[k].b[4][4]=1;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
    a[k]=a[k<<1]+a[k<<1|1];
}
martix query(int l,int r,int L,int R,int k)
{
    if(l<=L&&R<=r) return a[k];
    int mid=(L+R)/2;
    if(r<=mid) return query(l,r,L,mid,2*k);
    else if(l>mid) return query(l,r,mid+1,R,2*k+1);
    return query(l,mid,L,mid,2*k)+query(mid+1,r,mid+1,R,2*k+1);
}
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        scanf("%s",s+1);
        build(1,n,1);
        while(q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int ans=query(l,r,1,n,1).b[0][4];
            if(ans>=n) puts("-1");
            else printf("%d\n",ans);
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值