10.01QBXT集训
a
【问题描述】
你是能看到第一题的 friends呢。
—— hja
何大爷对字符串十分有研究,于是天出题虐杀 zhx。何大爷今天为 何大爷今天为 字符串定义了新的权值计算方法。一个由小写母组成,被定义为其中出现次数最多的字符减去少。(注 被定义为其中出现次数最多的字符减去少。(注 意,在讨论出现最少的字符时候该必须至一次)何大爷给你一个字符串,何大爷想知道这的所有子中权值最是多少?
【输入格式】
第一行 一个整数 𝑛,代表字符串的长度 。
接下来一行 𝑛个小写字母,代表该符串。
【输出格式】
一行个整数代表答案。
【样例输入】
10
aabbaaabab
【样例输出】
3
【数据范围与规定】
对于 30%的数据, 1≤𝑛≤100。
对于 60%的数据, 1≤𝑛≤1000。
对于 100%的数据, 1≤𝑛≤10^6.
思路分析:大概需要一个前缀和维护最大值最小值。
30分做法:两层for循环枚举l与r,暴力统计l到r之间的字符出现次数,然后暴力排序一次,输出答案。
60分做法:右端点每次都移动1个位置,所以可以使用前缀和维护最大值最小值。对每个字符预处理前缀和。
100分做法:对于每一个字符记录前缀和,然后空间复杂度大概是$O(26\times \text{str.length()}$.时间复杂度可以降到$O(26*2*n)$。
code by zhx, comment by Mingqi_H.
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<vector> 5 6 using namespace std; 7 8 const int maxn=1000010; 9 10 int n,ans,p[26][26],minv[26][26],sum[26],last[26]; 11 12 char s[maxn]; 13 14 int main() 15 { 16 // freopen("a.in","r",stdin); 17 // freopen("a.out","w",stdout); 18 19 scanf("%d",&n); 20 scanf("%s",s+1); 21 for (int a=1;a<=n;a++) 22 { 23 int c=s[a]-'a';//暂存一下字符串的当前字符 24 sum[c]++;//对这个字符进行前缀和处理 25 last[c]=a;//保存这个字符最后一次出现的位置 26 for (int b=0;b<26;b++) 27 if (b!=a && sum[b])//如果端点不重合(至少取两个字符构成子串),并且这个字符出现过 28 ans=max(ans, 29 max(sum[c]-sum[b]-minv[c][b]-(last[b]==p[c][b]), 30 sum[b]-sum[c]-minv[b][c]-(last[b]==p[b][c]))); 31 for (int b=0;b<26;b++)//对于每一个字符 32 { 33 if (sum[c]-sum[b]<minv[c][b]) 34 minv[c][b]=sum[c]-sum[b],p[c][b]=a;//前缀和维护一下最小值 35 if (sum[b]-sum[c]<minv[b][c]) //同上 36 minv[b][c]=sum[b]-sum[c],p[b][c]=a; 37 } 38 } 39 printf("%d\n",ans); 40 41 return 0; 42 }
附get到的dalao听课笔记:
远超NOIPT1 30:枚举l,r 两重for,将区间扫一遍 60:思考这一做法 右端点每次向右移动一个位置 因此每一个字符都可以预处理前缀和 这样可以拿到60分 70:上述做法需要$26^2$次 由于对某一字符对另25个字符没有必要枚举 可以优化一下(并没有听懂 90:考虑假设字符串s,希望算得[L,R]的权值 这就相当于统计字符的频次 对每一个字符记录一个前缀和 则quanzhi[l][r]为sum[k][r]-sum[k][l-1]-sum[j][r]+sum[j][l-1] 若枚举R,我们希望找到L使得结果最大 就可以让sum[k][l-1]-sum[j][l-1] 这里可以再找一个前缀最小值维护 即sum[k][j][x]表示前x个sum[k][x]-sum[j][x] 复杂度O(n*26*26) 100:同70,O(n*26)
b
【问题描述】
你是能看到第二题的 friends呢。
—— laekov
Hja和 Yjq在玩捉迷藏。 在玩捉迷藏。 Yjq躲了起来, Hja要找他。在们玩游戏的房间 里,只有一堵不透明的墙和个双面镜子。 里,只有一堵不透明的墙和个双面镜子。 Hja和 Yjq可以看作平面上坐标分 别为 (𝑋𝑣,𝑌𝑣)和(𝑋𝑝,𝑌𝑝)的点。墙是一条连接 的点。墙是一条连接 (𝑋𝑤1,𝑌𝑤1)和(𝑋𝑤2,𝑌𝑤2)的线段,镜子是 的线段,镜子是 一条连接 (𝑋𝑚1,𝑌𝑚1)和(𝑋𝑚2,𝑌𝑚2)的线段。
如果 视线和障碍物有公共点,那么我们认为会被阻挡无法看见。如果 视线和障碍物有公共点,那么我们认为会被阻挡无法看见。如果 视线和障碍物有公共点,那么我们认为会被阻挡无法看见。如果 视线和障碍物有公共点,那么我们认为会被阻挡无法看见。视线和镜子有公共点,那么我们认为发生了反射。的过程遵循物理规律 视线和镜子有公共点,那么我们认为发生了反射。的过程遵循物理规律 视线和镜子有公共点,那么我们认为发生了反射。的过程遵循物理规律 —— 入射角等于反,且光线与在镜子同侧。也就是说想要看见对 入射角等于反,且光线与在镜子同侧。也就是说想要看见对 入射角等于反,且光线与在镜子同侧。也就是说想要看见对 入射角等于反,且光线与在镜子同侧。也就是说想要看见对 方, Hja和 Yjq必须在镜子的同一侧,包括所直线上(参见样例 必须在镜子的同一侧,包括所直线上(参见样例 必须在镜子的同一侧,包括所直线上(参见样例 1)。 如果 视线与镜子重合,那么不会发生反射并且被当作障碍物(参见样例 视线与镜子重合,那么不会发生反射并且被当作障碍物(参见样例 视线与镜子重合,那么不会发生反射并且被当作障碍物(参见样例 视线与镜子重合,那么不会发生反射并且被当作障碍物(参见样例 4)。
Hja很想知道他站在原地能否看见 Yjq,帮助他解决这个问题。
【输入格式】
第一行两个数 𝑋𝑣,𝑌𝑣,表示 Hja的坐标。
第二行两个数 𝑋𝑝,𝑌𝑝表示 Yjq的坐标。
第三行四个数 𝑋𝑤1,𝑌𝑤1,𝑋𝑤2,𝑌𝑤2,分别表示墙的两个端点坐标。
第四行个数 𝑋𝑚1,𝑌𝑚1,𝑋𝑚2,𝑌𝑚2,分别表示镜子的两个端点坐标。
【输出格式】
如果 Hja站在原地能看到 Yjq,则输出 "YES",否则输出 "NO"。
【样例输入1】
-1 3
1 3
0 2 4
0 1
【样例输出1】
NO
【样例输入2】
0
1
0
1 -100 -100 -101 -101
【样例输出2】
NO
【样例输入3】
0
1
0
1 -1 3
【样例输出3】
YES
【样例输入4】
0
10 0
100 101
1 0
3 【样例输出4】
YES
【数据规模与约定】
对于 100%的数据, 所有坐标均为绝对值不超过 所有坐标均为绝对值不超过 104的整数。输入线段不会 的整数。输入线段不会 退化成点,且两条线段没有交。 退化成点,且两条线段没有交。 Hja和 Yjq的位置不同,且在任何一条线段 的位置不同,且在任何一条线段 上
思路分析:一看...这不计算几何吗?随机输出一下搞一点事情。预估得分:44-66分(考试得分:46),当然更换一下随机算法还能更好一些(输出NO得分54)。但对于多组数据gg.
正解计算几何:已知能看到只有两种情况。先判断一下是否有墙、是否在镜子的两侧,如果遇到这种情况就直接输出NO,否则作点关于镜子所在直线的对称点,然后再连一条边,判断是否与镜子有交点,如果有交点就输出YES,否则输出NO。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const double eps=1e-8; 8 int sgn(double a) 9 { 10 if (fabs(a)<eps) return 0; 11 else 12 { 13 if (a>0.0) return 1; 14 else return -1; 15 } 16 } 17 struct point 18 { 19 double x,y; 20 point(){} 21 point(double a,double b) 22 { 23 x=a;y=b; 24 } 25 void init() 26 { 27 scanf("%lf%lf",&x,&y); 28 } 29 point operator+(const point &a)const 30 { 31 point ans; 32 ans.x=x+a.x; 33 ans.y=y+a.y; 34 return ans; 35 } 36 point operator-(const point &a)const 37 { 38 point ans; 39 ans.x=x-a.x; 40 ans.y=y-a.y; 41 return ans; 42 } 43 point operator*(const double &a)const 44 { 45 point ans; 46 ans.x=x*a; 47 ans.y=y*a; 48 return ans; 49 } 50 void print() 51 { 52 printf("%lf %lf\n",x,y); 53 } 54 }v,p,w1,w2,m1,m2; 55 double cross(point a,point b) 56 { 57 return a.x*b.y-a.y*b.x; 58 } 59 double dot(point a,point b) 60 { 61 return a.x*b.x+a.y*b.y; 62 } 63 bool cross(point p1,point p2,point p3,point p4) 64 { 65 if (sgn(cross(p2-p1,p3-p1))*sgn(cross(p2-p1,p4-p1))==1) return false; 66 if (sgn(cross(p4-p3,p1-p3))*sgn(cross(p4-p3,p2-p3))==1) return false; 67 if (sgn(max(p1.x,p2.x)-min(p3.x,p4.x))==-1) return false; 68 if (sgn(max(p1.y,p2.y)-min(p3.y,p4.y))==-1) return false; 69 if (sgn(max(p3.x,p4.x)-min(p1.x,p2.x))==-1) return false; 70 if (sgn(max(p3.y,p4.y)-min(p1.y,p2.y))==-1) return false; 71 return true; 72 } 73 point getcross(point p1,point p2,point p3,point p4) 74 { 75 double a=p2.y-p1.y; 76 double b=p1.x-p2.x; 77 double c=-p1.x*p2.y+p1.y*p2.x; 78 double d=p4.y-p3.y; 79 double e=p3.x-p4.x; 80 double f=-p3.x*p4.y+p3.y*p4.x; 81 double x=(b*f-c*e)/(a*e-b*d); 82 double y=(a*f-c*d)/(b*d-a*e); 83 return point(x,y); 84 } 85 point calcfoot(point p1,point p2,point p3) 86 { 87 double ratio=dot(p1-p2,p3-p2)/dot(p3-p2,p3-p2); 88 return p2+(p3-p2)*ratio; 89 } 90 bool check() 91 { 92 if (!cross(v,p,w1,w2)) 93 { 94 if (!cross(v,p,m1,m2)) return true; 95 if (sgn(cross(m1-v,m2-v))==0 && sgn(cross(m1-p,m2-p)==0)) return true; 96 } 97 if (sgn(cross(m2-m1,v-m1))*sgn(cross(m2-m1,p-m1))==1) 98 { 99 point foot=calcfoot(p,m1,m2); 100 foot=foot*2.0-p; 101 if (cross(v,foot,m1,m2)) 102 { 103 foot=getcross(v,foot,m1,m2); 104 if (!cross(v,foot,w1,w2) && !cross(foot,p,w1,w2)) return true; 105 } 106 } 107 return false; 108 } 109 int main() 110 { 111 freopen("b.in","r",stdin); 112 freopen("b.out","w",stdout); 113 v.init(); 114 p.init(); 115 w1.init(); 116 w2.init(); 117 m1.init(); 118 m2.init(); 119 if (check()) printf("YES\n"); 120 else printf("NO\n"); 121 return 0; 122 }
c
【问题描述】
你是能看到第三题的 friends呢。
—— aoao
众所周知 ,八数码 问题 是一个 非常 难的问题 ,但是 Yjq非常 有面子 ,他把这 道题 简化了 一番 。现在 给了 你一个 3×3的方格图 ,你的 目标 是通过 不断 移动 使得 相邻 颜色 的块形成联通 块。你每次 的移动 方式 是选择 一列 或者 一行 进行 置换 滑动 (这个 解释 起来 比较 麻烦 ,看下面 的图就懂了 )。所谓 置换 滑动 ,就是 所有 格子 沿着 给定 的方向 顺次 移动 ,最后 一个 格子 会被 置换到 最前面 的过程 。现在 给定 整 个方格图 ,以及 每个 格子 是否 能够 移动 ,求使得 相同 颜色 联通 的最小步数 。
【输入格式】
输入 为3×3的方格图 ,每个 位置 由五个字符 组成 ,前四个字符 分别 表示 上下 左右 四个 部分 的颜色 ,第五个 字符 表示 该格子 是否 能够 移动 ,其中 0是能移动 1是 不能移动 。
【输出格式】
一行个整数代表答案。
【样例输入】
GGGG0
OGOO0
GGGG0 OOOO0
OGGG1 【样例输出】
5
【样例解释】
【数据规模与约定】
对于 100%的数据 ,所有颜色 只可能是 RGBO中的一种 ,且一定 有解 。
思路分析:搜索。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<queue> 5 6 using namespace std; 7 8 #define get(a,b,c) ((a-1)*12+(b-1)*4+c) 9 10 int en,tmp[4][4],color[37],map[9][5],q[37],nowmap[4][4],newmap[4][4]; 11 12 bool num[9],use[90000000],right[37],row[4],col[4],col_find[5]; 13 14 char s[10]; 15 16 struct rec 17 { 18 int sta,step; 19 rec(){} 20 rec(int a,int b) 21 { 22 sta=a;step=b; 23 } 24 }; 25 26 queue<rec> que; 27 28 struct edge 29 { 30 int e; 31 edge *next; 32 }*v[37],ed[100]; 33 34 void add_edge(int s,int e) 35 { 36 en++; 37 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e; 38 en++; 39 ed[en].next=v[e];v[e]=ed+en;v[e]->e=s; 40 } 41 42 bool check(int nows) 43 { 44 memset(num,false,sizeof(num)); 45 for (int a=3;a>=1;a--) 46 for (int b=3;b>=1;b--) 47 if (a!=3 || b!=3) 48 { 49 tmp[a][b]=nows%10; 50 num[nows%10]=true; 51 nows/=10; 52 } 53 for (int a=0;a<9;a++) 54 if (!num[a]) 55 { 56 tmp[3][3]=a; 57 break; 58 } 59 int cnt=0; 60 for (int a=1;a<=3;a++) 61 for (int b=1;b<=3;b++) 62 for (int c=1;c<=4;c++) 63 { 64 cnt++; 65 color[cnt]=map[tmp[a][b]][c]; 66 } 67 memset(right,false,sizeof(right)); 68 memset(col_find,false,sizeof(col_find)); 69 for (int a=1;a<=36;a++) 70 if (!right[a]) 71 { 72 if (col_find[color[a]]) return false; 73 col_find[color[a]]=true; 74 int front=1,tail=1; 75 q[1]=a; 76 right[a]=true; 77 for (;front<=tail;) 78 { 79 int now=q[front++]; 80 for (edge *e=v[now];e;e=e->next) 81 if (color[e->e]==color[now] && !right[e->e]) 82 { 83 right[e->e]=true; 84 q[++tail]=e->e; 85 } 86 } 87 } 88 return true; 89 } 90 91 int main() 92 { 93 freopen("c.in","r",stdin); 94 freopen("c.out","w",stdout); 95 96 for (int a=1;a<=3;a++) 97 for (int b=1;b<=3;b++) 98 { 99 add_edge(get(a,b,1),get(a,b,3)); 100 add_edge(get(a,b,1),get(a,b,4)); 101 add_edge(get(a,b,2),get(a,b,3)); 102 add_edge(get(a,b,2),get(a,b,4)); 103 if (a!=3) add_edge(get(a,b,2),get(a+1,b,1)); 104 if (b!=3) add_edge(get(a,b,4),get(a,b+1,3)); 105 } 106 int cnt=0; 107 for (int a=1;a<=3;a++) 108 for (int b=1;b<=3;b++) 109 { 110 scanf("%s",s+1); 111 for (int c=1;c<=4;c++) 112 if (s[c]=='R') map[cnt][c]=0; 113 else 114 { 115 if (s[c]=='G') map[cnt][c]=1; 116 else 117 { 118 if (s[c]=='B') map[cnt][c]=2; 119 else map[cnt][c]=3; 120 } 121 } 122 if (s[5]=='1') row[a]=col[b]=true; 123 cnt++; 124 } 125 int nows=1234567; 126 if (check(nows)) 127 { 128 printf("0\n"); 129 return 0; 130 } 131 que.push(rec(nows,0)); 132 use[nows]=true; 133 rec now; 134 while (que.size()) 135 { 136 now=que.front(); 137 que.pop(); 138 int step=now.step; 139 int nows=now.sta; 140 memset(num,false,sizeof(num)); 141 for (int a=3;a>=1;a--) 142 for (int b=3;b>=1;b--) 143 if (a!=3 || b!=3) 144 { 145 nowmap[a][b]=nows%10; 146 num[nows%10]=true; 147 nows/=10; 148 } 149 for (int a=0;a<9;a++) 150 if (!num[a]) 151 { 152 nowmap[3][3]=a; 153 break; 154 } 155 int news=0; 156 for (int a=1;a<=3;a++) 157 { 158 if (!row[a]) 159 { 160 for (int b=1;b<=3;b++) 161 for (int c=1;c<=3;c++) 162 newmap[b][c]=nowmap[b][c]; 163 int x=newmap[a][1]; 164 newmap[a][1]=newmap[a][2];newmap[a][2]=newmap[a][3];newmap[a][3]=x; 165 news=0; 166 for (int b=1;b<=3;b++) 167 for (int c=1;c<=3;c++) 168 if (b!=3 || c!=3) news=news*10+newmap[b][c]; 169 if (!use[news]) 170 { 171 use[news]=true; 172 if (check(news)) 173 { 174 printf("%d\n",step+1); 175 return 0; 176 } 177 que.push(rec(news,step+1)); 178 } 179 x=newmap[a][1]; 180 newmap[a][1]=newmap[a][2];newmap[a][2]=newmap[a][3];newmap[a][3]=x; 181 news=0; 182 for (int b=1;b<=3;b++) 183 for (int c=1;c<=3;c++) 184 if (b!=3 || c!=3) news=news*10+newmap[b][c]; 185 if (!use[news]) 186 { 187 use[news]=true; 188 if (check(news)) 189 { 190 printf("%d\n",step+1); 191 return 0; 192 } 193 que.push(rec(news,step+1)); 194 } 195 } 196 if (!col[a]) 197 { 198 for (int b=1;b<=3;b++) 199 for (int c=1;c<=3;c++) 200 newmap[b][c]=nowmap[b][c]; 201 int x=newmap[1][a]; 202 newmap[1][a]=newmap[2][a];newmap[2][a]=newmap[3][a];newmap[3][a]=x; 203 news=0; 204 for (int b=1;b<=3;b++) 205 for (int c=1;c<=3;c++) 206 if (b!=3 || c!=3) news=news*10+newmap[b][c]; 207 if (!use[news]) 208 { 209 use[news]=true; 210 if (check(news)) 211 { 212 printf("%d\n",step+1); 213 return 0; 214 } 215 que.push(rec(news,step+1)); 216 } 217 x=newmap[1][a]; 218 newmap[1][a]=newmap[2][a];newmap[2][a]=newmap[3][a];newmap[3][a]=x; 219 news=0; 220 for (int b=1;b<=3;b++) 221 for (int c=1;c<=3;c++) 222 if (b!=3 || c!=3) news=news*10+newmap[b][c]; 223 if (!use[news]) 224 { 225 use[news]=true; 226 if (check(news)) 227 { 228 printf("%d\n",step+1); 229 return 0; 230 } 231 que.push(rec(news,step+1)); 232 } 233 } 234 } 235 } 236 237 return 0; 238 }