8.5 纪中集训 Day5
T1输油管道
Description
请你帮忙设计一个从城市M到城市Z的输油管道,现在已经把整个区域划分为R行C列,每个单元格可能是空的也可能是以下7种基本管道之一:
油从城市M流向Z,‘+’型管道比较特殊,因为石油必须在两个方向(垂直和水平)上传输,如下图所示:
现在恐怖分子弄到了输油管道的设计图,并把其中一个单元格中的管道偷走了,请你帮忙找到偷走的管道的位置以及形状。
油从城市M流向Z,‘+’型管道比较特殊,因为石油必须在两个方向(垂直和水平)上传输,如下图所示:
现在恐怖分子弄到了输油管道的设计图,并把其中一个单元格中的管道偷走了,请你帮忙找到偷走的管道的位置以及形状。
Input
第一行包含两个整数R和C(1<=R,C<=25)。
接下来R行每行C个字符描述被偷之后的形状,字符分为以下三种:
(1)‘.’表示空;
(2)字符‘|’(ASCII为124)、‘-’、‘+’、‘1’、‘2’、‘3’、‘4’描述管道的形状;
(3)‘M’和‘Z’表示城市,两个都是只出现一次。
输入保证石油的流向是唯一的,只有一个管道跟M和Z相连,除此此外,保证没有多余的管道,也就是说所有的管道在加进被偷的管道后一定都会被用上。
输入保证有解而且是唯一的。
接下来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个数移到各自的目标位置,编程计算每个数需要几次操作。
(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必须按照输入顺序依次移动。
接下来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天后一共灌了几次水。
•如果前一天被灌了奇数次的水,他们将会给每个朋友灌一次水;
•如果前一天被灌了偶数次的水,他们将会给每个朋友灌两次水。
学生编号为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’表示不是。注意自己和自己不是朋友关系,输入保证该矩阵是对称的。
接下来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。
样例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的示意图:
每天都有一个新植物长出来,第一天的植物高度为1,后面每天长出的植物比前一天的高1。
当一个新植物长出来的时候,跟其他植物的水平线段相交处会长出一朵小花(前提是之前没有长出花朵),如果线段交于端点,是不会长花的。
下图为样例1的示意图:
给出每天的植物的坐标,计算每天长出多少新花。
Input
第一行包含一个整数N(1<=N<=100000),表示天数。
接下来N行,每行两个整数L和R(1<=L<=R<=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 天 祭