写在前面:
我头好痛
考试概况:
T1:数论 tps:60 耗时:1h
T2:数论+组合数学 tps:0 耗时:10min
T3:二分图 tps:60 耗时:2h
sum : 120 rank:13
暴力分其实还能多写30 > . <
T1 方程的解
数论一直学的很死,记得扩欧会出负数,然后就傻了,不会,直接跳题
T2 visit
一眼组合数学,然后不会写~~~~(其实是自己根本不会组合)
对于30分,dp[走到哪行][走到哪列][花了几步],记得处理好循环上限。
正解是,对于T=n+m的情况,看为在n+m步中挑n步为横着走,答案为C(n+m,n),然后接着往下想,对于多出来的步数要怎么办呢?
因为不能站着不动,所以只能左右横跳或者上下横跳(?)来抵消步数,如果T-n-m为奇数,则不可能停在目标点,直接去掉
对于每多出来的两步,可以选择加一组上下或者一组左右,枚举再进行下一步处理
把路径拆成向各个方向移动的步数,然后就是一个简单的组合数问题
那这跟数论有啥关系啊?
有,因为他很巧(e)妙(xin)地把40%的数据中的模数变为了几个不同质数的乘积
最后要来个中剩(太恶毒了,衡水rank1大哥居然还A了,非人哉)
代码我可以咕咕咕吗
T3 CF274E mirror room
这是一道好题,但是我不会写> . <
很棒的事情是有60分的暴力分,然后我写了一个头都晕了的大暴力
#include<bits/stdc++.h> using namespace std ; const int MAXN = 2050; int n,m,k,sx,sy,dir; int dx[5] = {0,-1,1,1,-1},dy[5] = {0,1,1,-1,-1}; int cx1[5] = {0,0,-1,0,1},cy1[5] = {0,-1,0,1,0}; int cx2[5] = {0,1,0,-1,0},cy2[5] = {0,0,-1,0,1}; int fd0[5] = {0,3,4,1,2},fd1[5] = {0,2,3,4,1},fd2[5] = {0,4,1,2,3},fd3[5] = {0,3,4,1,2}; int nx1[5] = {0,0,1,0,-1} , ny1[5] = {0,1,0,-1,0},nx2[5] = {0,-1,0,1,0} , ny2[5] = {0,0,1,0,-1}; bool book[MAXN][MAXN][5],blo[MAXN][MAXN]; int main(){ ios::sync_with_stdio(false); cin>>n>>m>>k; for(int i=1;i<=k;i++){ int xx,yy; cin>>xx>>yy; blo[xx][yy] = 1; } char direc[5]; cin>>sx>>sy; cin>>direc; if(direc[0] == 'S' && direc[1] == 'E') dir = 2; if(direc[0] == 'S' && direc[1] == 'W') dir = 3; if(direc[0] == 'N' && direc[1] == 'E') dir = 1; if(direc[0] == 'N' && direc[1] == 'W') dir = 4; for(int i=0;i<=n+1;i++){ blo[i][0] = blo[i][m+1] = 1; } for(int i=0;i<=m+1;i++){ blo[0][i] = blo[n+1][i] = 1; } while(1){ if(book[sx][sy][dir]) break; book[sx][sy][dir] = true; if(blo[sx+dx[dir]][sy+dy[dir]]){ bool flag1=blo[sx + dx[dir] + cx1[dir]][sy + dy[dir] + cy1[dir]]; bool flag2=blo[sx + dx[dir] + cx2[dir]][sy + dy[dir] + cy2[dir]]; if(flag1 && flag2) dir = fd3[dir]; else if(flag1 && !flag2) sx += nx1[dir],sy += ny1[dir],dir = fd1[dir]; else if(!flag1 && flag2) sx += nx2[dir],sy += ny2[dir],dir = fd2[dir]; else if(!flag1 && !flag2) dir = fd0[dir]; } else sx+=dx[dir],sy+=dy[dir]; } int ans = 0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(blo[i][j]) continue; for(int k=1;k<=4;k++){ if(book[i][j][k]){ ans++; break; } } } } cout<<ans<<endl; return 0; }
百万手码数组
正解瞎看了看,不知道为什么复杂度是对的> . <
为了方便思考我们以格子中心为思考对象
首先区别两种反射方式:斜路反射(方向和所在格子都改变的3)和回路反射(只改变方向的2 4)
(第一种不算反射)
将图染色,有邻边的方格染不同颜色,染成黑白,显然光线不经过斜路反射无法到达另一种颜色的路径,
然后因为从一个格点到另一个格点时,格点的横纵坐标之和不变,一个小格子的两对对角点奇偶性不同,
所以一个格子只有两种经过方式(斜入斜出)
如果模拟每次反射,由上面的结论易得复杂度最坏为O(n+m+k),这很好。
在一次循环内统计在下一次斜路反射前经过多少格子,如果出现了回路反射证明原来起点的反方向也可以来一遍,
不断统计答案(也许题解是这个意思?)
TAG:SIN_XIII ⑨