8.5 纪中集训 Day5

T1输油管道

Description

  请你帮忙设计一个从城市M到城市Z的输油管道,现在已经把整个区域划分为R行C列,每个单元格可能是空的也可能是以下7种基本管道之一:
  

  油从城市M流向Z,‘+’型管道比较特殊,因为石油必须在两个方向(垂直和水平)上传输,如下图所示:

        


  现在恐怖分子弄到了输油管道的设计图,并把其中一个单元格中的管道偷走了,请你帮忙找到偷走的管道的位置以及形状。

Input

  第一行包含两个整数R和C(1<=R,C<=25)。
  接下来R行每行C个字符描述被偷之后的形状,字符分为以下三种:
  (1)‘.’表示空;
  (2)字符‘|’(ASCII为124)、‘-’、‘+’、‘1’、‘2’、‘3’、‘4’描述管道的形状;
  (3)‘M’和‘Z’表示城市,两个都是只出现一次。
  输入保证石油的流向是唯一的,只有一个管道跟M和Z相连,除此此外,保证没有多余的管道,也就是说所有的管道在加进被偷的管道后一定都会被用上。
  输入保证有解而且是唯一的。

Output

  输出被偷走的管道的行号和列号以及管道的类型。

Sample Input

输入1:
3 7
.......
.M-.-Z.
.......

输入2:
3 5
..1-M
1-+..
Z.23.

输入3:
6 10
Z.1----4..
|.|....|..
|..14..M..
2-+++4....
..2323....
..........

Sample Output

输出1:
2 4 -

输出2:
2 4 4

输出3:
3 3 |

考场思路\正解

签到题,不过考试时候让我签到得有点久,导致后面题目来不及做。

模拟暴力,不多说。

Code

我觉得我的代码又长又臭

#include<stdio.h>
#include<algorithm>
using namespace std;

int n,m;
int Next[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
char tu[25][25];

void Find_wrong(int x,int y)
{
    printf("%d %d ",x,y);
    int a=0,b=0,c=0,d=0;  
    if(tu[x-1][y]=='1' || tu[x-1][y]=='4' || tu[x-1][y]=='|' || tu[x-1][y]=='+')
        a=1;
    if(tu[x][y+1]=='3' || tu[x][y+1]=='4' || tu[x][y+1]=='-' || tu[x][y+1]=='+')
        b=1;
    if(tu[x+1][y]=='2' || tu[x+1][y]=='3' || tu[x+1][y]=='|' || tu[x+1][y]=='+')
        c=1;
    if(tu[x][y-1]=='1' || tu[x][y-1]=='2' || tu[x][y-1]=='-' || tu[x][y-1]=='+')
        d=1;
    if(a && b && c && d)
        <%printf("+");exit(0);%>
    if(a && c)
        <%printf("|");exit(0);%>
    if(b && d)
        <%printf("-");exit(0);%>
    if(b && c)
        <%printf("1");exit(0);%>
    if(a && b)
        <%printf("2");exit(0);%>
    if(a && d)
        <%printf("3");exit(0);%>
    if(c && d)
        <%printf("4");exit(0);%>
}

void Work(int x,int y,int di) 
{
    if(tu[x][y]=='.')
        <%Find_wrong(x,y);return;%>
    if(di==1)  /
    {
        if(tu[x][y]=='+' || tu[x][y]=='|')
            Work(x+1,y,1);
        if(tu[x][y]=='2')
            Work(x,y+1,2);
        if(tu[x][y]=='3')
            Work(x,y-1,4);
        return;
    }
    if(di==2) 
    {
        if(tu[x][y]=='+' || tu[x][y]=='-')
            Work(x,y+1,2);
        if(tu[x][y]=='3')
            Work(x-1,y,3);
        if(tu[x][y]=='4')
            Work(x+1,y,1);
        return;
    }
    if(di==3)
    {
        if(tu[x][y]=='+' || tu[x][y]=='|')
            Work(x-1,y,3);
        if(tu[x][y]=='1')
            Work(x,y+1,2);
        if(tu[x][y]=='4')
            Work(x,y-1,4);
        return;
    }
    if(di==4)
    {
        if(tu[x][y]=='+' || tu[x][y]=='-')
            Work(x,y-1,4);
        if(tu[x][y]=='1')
            Work(x+1,y,1);
        if(tu[x][y]=='2')
            Work(x-1,y,3);
        return;
    }
}

int main()
{
    int i,j,x,y,tx,ty;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%s",tu[i]+1);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(tu[i][j]=='M')
                <%x=i,y=j;break;%>
    for(i=0;i<4;i++)
    {
        tx=x+Next[i][0],ty=y+Next[i][1];
        if(tx<1 || ty<1 || tx>n || ty>m)
            continue;
        if(tu[tx][ty]!='.')
            <%Work(tx,ty,i+1);break;%>  
    }
    if(i==4)
    {
        for(i=0;i<4;i++)
        {
            tx=x+Next[i][0],ty=y+Next[i][0];
            if(tx<1 || ty<1 || tx>n || ty>m)
                continue;
            Find_wrong(tx,ty);
        }
    }
    return 0;
}

T2 数码问题

Description

  Alice有一个N*N的格子,把1-N^2按照从上到下从左到右的顺序填进表格中,允许在表格上进行两种操作:
  (1) 旋转行——这一行的数向右移动一个位置,而最后一列的数会移到第一列;
  (2) 旋转列——这一列的数向下移动一个位置,最后一行的数会移到第一行。
  Alice想把数X移到(R,C)处可以采用以下方法:
  •如果X不在C这一列,通过旋转行操作把X移到C这一列;
  •如果X不在R这一行,通过旋转列操作把X移到R这一行。
  下面是一个把6移到(3,4)的例子:
  
  Alice现在想采用上述方法,依次把K个数移到各自的目标位置,编程计算每个数需要几次操作。

Input

  第一行包含两个整数N(12<=N<=10000)和K(1<=K<=1000)。
  接下来K行,每行包含三个整数X(1<=X<=N^2)、R和C(1<=R,C<=N),描述需要移动的数以及目标位置。
  Alice必须按照输入顺序依次移动。

Output

  输出K行,每行输出一个整数,表示操作次数。

Sample Input

输入1:
4 1
6 3 4

输入2:
4 2
6 3 4
6 2 2

输入3:
5 3
1 2 2
2 2 2
12 5 5

Sample Output

输出1:
3

输出2:
3
5

输出3:
2
5
3

考场思路

考试时候把题目看错了,以为是要将所有目标块同时归位,没有想到这么简单。

正解

暴力,把每次的操作记录下来,每询问一个数时,先将它把之前与它有关的操作做一遍,然后在处理。

Code

后面改的时候由于一个地方没理解清楚,所以代码略长,不要在意。

#include<cstdio>
#include<algorithm>
using namespace std;

int n,k,sx,sy,nowx,nowy,ans,sl;
int zx[1010],r[1010],c[1010];
struct thm
{
    int move;
    int dir;
    int kg;
}xy[2020];

void Move()
{
    int i=1;
    while(i<=sl)
    {
        if(sx==xy[i].dir && xy[i].kg==1)
        {
            sy+=xy[i].move;
            if(sy<1)    sy+=n;
            if(sy>n)    sy%=n;
        }
        if(sy==xy[i].dir && xy[i].kg==-1)
        {
            sx+=xy[i].move;
            if(sx<1)    sx+=n;
            if(sx>n)    sx%=n;
        }
        i++;
    }
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++)
        scanf("%d%d%d",&zx[i],&r[i],&c[i]);
    for(int i=1;i<=k;i++)
    {
        if(zx[i]%n)
            sx=zx[i]/n+1,sy=zx[i]%n;
        else
            sx=zx[i]/n,sy=n;
        Move();
        ans=0;
        if(sy<c[i])
        {
            xy[++sl].kg=1;
            xy[sl].dir=sx;
            xy[sl].move=c[i]-sy;
            ans+=c[i]-sy;

        }
        if(sy>c[i])
        {
            xy[++sl].kg=1;
            xy[sl].dir=sx;
            xy[sl].move=c[i]+n-sy;
            ans+=c[i]+n-sy;
        }
        if(sx<r[i])
        {
            xy[++sl].kg=-1;
            xy[sl].dir=c[i];
            xy[sl].move=r[i]-sx;
            ans+=r[i]-sx;
        }
        if(sx>r[i])
        {
            xy[++sl].kg=-1;
            xy[sl].dir=c[i];
            xy[sl].move=r[i]+n-sx;
            ans+=r[i]+n-sx;
        }
        printf("%d\n",ans);
    }
    return 0;
}

T3灌水

Description

  学生都很喜欢灌水,第一天只有Alice给她的每个朋友灌了一次水,从第二天开始,所有学生(包括Alice)将会有规律地去灌水:
  •如果前一天被灌了奇数次的水,他们将会给每个朋友灌一次水;
  •如果前一天被灌了偶数次的水,他们将会给每个朋友灌两次水。
  学生编号为1到N,Alice为1号,学生之间的朋友关系会给出。
  计算H天后一共灌了几次水。

Input

  输入一行包含两个整数N和H(1<=N<=20,1<=H<=10^9),表示学生数和天数。
  接下来N行,每行包含N个‘0’或‘1’,(A,B)处的字符表示A和B的关系,‘1’表示是朋友关系,‘0’表示不是。注意自己和自己不是朋友关系,输入保证该矩阵是对称的。

Output

  输出H天后一共灌水的数量。

Sample Input

输入1:
4 1
0110
1001
1001
0110

输入2:
4 2
0110
1001
1001
0110

输入3:
5 3
01000
10110
01000
01001
00010

Sample Output

输出1:
2

输出2:
14

输出3:
26

Hint

【样例解释】
  样例2中,第一天Alice灌了2次水,第二天学生1和学生4给学生2和学生3都灌了2次水,而学生2和学生3给学生1和学生4各灌水1次,2天一共灌了12次水。
【数据范围】
  50%的数据 H<=1000。

考场思路

考试时只瞄了一下题,来不及思考,所以没有思路。

正解

看一下数据范围,不难发现此题每个人的奇偶情况可以用二进制来压缩状态,然后找循环节来巧妙解决。

不过我改了一个晚上,因为xhs的代码欠砍,我拿他的代码对着调试,结果状态一直对不上,结果最后他告诉我他01所表示的奇偶和我是相反的,臭逼!然后我改了一个字母就A了。(不过我的代码比他短,嘿嘿)

Code

#include<cstdio>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;

LL n,h,jt,k,ans;
LL keep[2000020],book[2000020];
char ch[21][21];

unsigned sl[21];
vector<LL> rel[21];

void Work(LL x)
{
    for(unsigned i=0;i<sl[x];i++)
        k=k^(1<<(rel[x][i]-1));
}

LL interesting(LL i,LL jt)
{
    LL len=h-book[jt],zhi=keep[i]-keep[book[jt]],xhj=i-book[jt];
    LL a=(len/xhj-1)*zhi,b=0;
    if(len%xhj)
        b=keep[len%xhj+book[jt]]-keep[book[jt]];
    return a+b;
}

int main()
{
    scanf("%lld%lld",&n,&h);
    for(LL i=1;i<=n;i++)
        scanf("%s",ch[i]+1);
    for(LL i=1;i<=n;i++)
        for(LL j=1;j<i;j++)
            if(ch[i][j]=='1')
                rel[i].push_back(j),rel[j].push_back(i);
    for(LL i=1;i<=n;i++)
        sl[i]=rel[i].size();
    Work(1);
    jt=k,ans=(LL)sl[1];
    for(LL i=2;i<=h;i++)
    {
        k=0;
        for(LL j=1;j<=n;j++)
            ans+=(LL)sl[j]*((((jt&(1<<(j-1)))>>(j-1))^1)+1);  
        keep[i]=ans;
        for(LL j=1;j<=n;j++)
            if((jt&(1<<(j-1)))>>(j-1))
                Work(j);
        jt=k;
        if(!book[jt])
            book[jt]=i;
        else
            return printf("%lld",ans+interesting(i,jt)),0;
    }
    printf("%lld",ans);
    return 0;
}

T4开花

Description

  在遥远的火星上,上面的植物非常奇怪,都是长方形的,每个植物用三个数来描述:左边界L、右边界R以及高度H,如下图所示描述一个植物:L=2,R=5和H=4。
  
  每天都有一个新植物长出来,第一天的植物高度为1,后面每天长出的植物比前一天的高1。
  当一个新植物长出来的时候,跟其他植物的水平线段相交处会长出一朵小花(前提是之前没有长出花朵),如果线段交于端点,是不会长花的。
  下图为样例1的示意图:
  
给出每天的植物的坐标,计算每天长出多少新花。

Input

  第一行包含一个整数N(1<=N<=100000),表示天数。
  接下来N行,每行两个整数L和R(1<=L<=R<=100000),表示植物的左右边界。

Output

  输出每天长出新植物后增加新花的数量。

Sample Input

输入1:
4
1 4
3 7
1 6
2 6

输入2:
5
1 3
3 5
3 9
2 4
3 8

Sample Output

输出1:
0
1
1
2

输出2:
0
0
0
3
2

考场思路/正解

线段树,很明显,but 考试时却没有时间打,唉。

Code

#include<cstdio>
#include<algorithm>
#define MAXN 100010
using namespace std;

int n,ans,Max;
int tj[MAXN];
int l,r,x[MAXN],y[MAXN];
struct TREE
{
    int l,r;
    int zhi;
    int lazy;
}tree[8*MAXN];

void Build(int L,int R,int k)
{
    int Mid;
    tree[k].l=L;
    tree[k].r=R;
    if(L==R)
        return;
    Mid=(L+R)/2;
    Build(L,Mid,k*2);
    Build(Mid+1,R,k*2+1);
}

void Pushdown(int k)
{
    tree[k*2].zhi+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);
    tree[k*2+1].zhi+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);
    tree[k*2].lazy+=tree[k].lazy;
    tree[k*2+1].lazy+=tree[k].lazy;
    tree[k].lazy=0;
}

int Find(int L,int R,int k,int g)
{
    Pushdown(k);
    if(L==R && L==g)
        return tree[k].zhi;
    int Mid=(L+R)/2;
    if(g<=Mid)
        return Find(L,Mid,k*2,g);
    else
        return Find(Mid+1,R,k*2+1,g);
}

void Add(int L,int R,int k)
{
    Pushdown(k);
    int Mid;
    if(l<=L && R<=r)
    {
        tree[k].lazy+=1;
        tree[k].zhi+=(R-L+1);
        return;
    }
    Mid=(R+L)/2;
    if(l<=Mid)
        Add(L,Mid,k*2);
    if(Mid<r)
        Add(Mid+1,R,k*2+1);
    tree[k].zhi=tree[k*2].zhi+tree[k*2+1].zhi;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&x[i],&y[i]),Max=max(Max,y[i]);
    Build(1,Max,1);
    for(int i=1;i<=n;i++)
    {
        l=x[i],r=y[i];
        int a=Find(1,Max,1,l);
        int b=Find(1,Max,1,r);
        printf("%d\n",a+b-tj[l]-tj[r]);
        tj[l]=a,tj[r]=b;
        l++,r--;
        if(l<=r)
            Add(1,Max,1);
    }
    return 0;
}

距 NOIp2019 还剩 95 天        祭

 

 
posted @ 2019-08-05 22:07  Thm-V  阅读(147)  评论(0编辑  收藏  举报