概述
比赛传送门
作者: fn
目录
- 签到题
- 1001题 Calculus 微积分学
- 基本题
- 1009题 License Plate Recognition 车牌识别
- 1002题 Kanade Loves Maze Designing Kanade热爱迷宫设计
- 进阶题
- 1008题 Lawn of the Dead 死亡草坪
- 1004题 Display Substring 显示子串
签到题
1001题 Calculus 微积分学
题目大意
判断级数是否收敛
考察内容
签到,思维
分析
注意到题中所给的所有函数的级数均发散。所以只需要检查是否所有的构成函数的系数均为 0 即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m;
int main(){
int t; cin>>t;
while(t--){
string s;
cin>>s;
ll len=s.size();
int flag=1; // 收敛
{
ll j=0;
ll cnt=0;
while(s[j]>='0' && s[j]<='9'){
cnt=cnt*10+(s[j]-'0');
j++;
}
if(cnt!=0){
puts("NO");
continue;
}
}
int j=0;
for(int i=0;i<len;i=j){ //
j=i+1;
if(s[i]=='+'){
ll cnt=0;
while(s[j]>='0' && s[j]<='9'){
cnt=cnt*10+(s[j]-'0');
j++;
}
if(cnt!=0){
flag=0; // 发散
break;
}
}
}
if(flag)puts("YES");
else puts("NO");
}
return 0;
}
基本题
1009题 License Plate Recognition 车牌识别
题目大意
给出一个7个字符的车牌号,取到每个字符的左右端点。
考察内容
模拟,暴力
分析
扫描一遍。可以通过判断宽度或者识别汉字特征来处理第一个汉字 “川” “鄂“ 的特判。
Code1(判断宽度>9):
#include<bits/stdc++.h>
using namespace std;
int t,d;
char s[40][110];
void init()
{
for(int i=1;i<=30;i++){
for(int j=1;j<=100;j++){
cin>>s[i][j];
}
}
return;
}
int c(int k)
{
for(int i=1;i<=30;i++){
if(s[i][k]=='#') return 1;
}
return 0;
}
int work(int left,int right)
{
if(left==0 || right==0) return 0;
if(d==0 && right-left<=9) return 0;
cout<<left<<" "<<right<<endl;
return 1;
}
int main()
{
cin>>t;
int tap=1;
while(t--)
{
init();
cout<<"Case #"<<tap<<":"<<endl;
tap++;
d=0;
int left=0,right=0,ll=1;
while(ll<=100)
{
if(c(ll)==0 || ll==100){
if(ll==100 && c(ll)==0) right=99;
if(ll==100 && c(ll)==1) right=100;
if(work(left,right)==1){
left=0;right=0;d++;
}
}
else
{
if(left==0) left=ll;
else right=ll;
}
ll++;
}
}
return 0;
}
Code2(识别汉字特征进行特判):
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m;
int l[50],r[50];
int main(){ //
// freopen("1.out","w",stdout); //
int t; cin>>t;
int Case=1;
while(Case<=t){
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
string s[40];
for(int i=1;i<=30;i++){
cin>>s[i];
}
cout<<"Case #"<<Case<<":"<<endl;
Case++;
//
int flag=0;
int p=1,q=1;
for(int j=0;j<100;j++){
int F=0;
for(int i=1;i<=30;i++){
if(s[i][j]=='#'){ // 出现
F=1;
}
}
if(flag==0 && F==1){ // 起点
flag=1; // 当前有字
l[p]=j+1; // 记录左端点
p++;
// 特判
if(p==2){ // 刚刚记录了第一个汉字
int num[4]={0};
int pos[40]={0};
int p1=1;
for(int i=1;i<=30;i++){
if(s[i][j]=='#'){
num[1]++;
pos[p1]=i;
p1++;
}
if(s[i][j+1]=='#'){
num[2]++;
}
if(s[i][j+2]=='#'){
num[3]++;
}
}
// 判断川
if(num[1]==2 && num[2]==4 && num[3]==11 && pos[2]==pos[1]+1){
if(s[pos[1]][j+1]=='#' && s[pos[1]-1][j+1]=='#' && s[pos[1]-2][j+1]=='#' && s[pos[1]-3][j+1]=='#'){
j+=13; // 跳过这段
}
}
// 判断鄂
if(num[1]==6 && num[2]==5 && num[3]==6 && pos[2]==pos[1]+1){
if(s[pos[1]][j+1]=='#' && s[pos[1]][j+2]=='#' && s[pos[1]][j+3]=='#'){ //
j+=13; // 跳过这段
}
}
}
continue;
}
if(flag==1 && F==0){
flag=0;
r[q]=j; // 记录右端点
q++;
}
}
if(flag==1){ // 当前有字
r[7]=100; // 最后一个位子放上
}
for(int i=1;i<=7;i++){
cout<<l[i]<<' '<<r[i]<<endl;
}
}
return 0;
}
1002题 Kanade Loves Maze Designing Kanade热爱迷宫设计
考察内容
dfs
分析
注意到允许一个
O
(
N
2
)
O(N^{2})
O(N2) 的算法,考虑用 ±1 法统计每种颜色。记
c
i
c_{i}
ci 为颜色
i
i
i 的出现次数,
r
r
r 为从某点处开始 DFS 到
i
i
i 点 得到的答案,进入该点时,给
c
i
c_{i}
ci 加一,如果这种颜色第一次出现,则给
r
r
r 加一,记录答案,退出该点时,如果给
r
r
r 减一后这种颜色不会出现,则
c
i
c_{i}
ci 减一。
#include<bits/stdc++.h>
#define MAXN 19560929
#define MOD1 1000000007
#define MOD2 1000000009
using namespace std;
int t,n,v[2001],num[2001],vv[2001];
int A[2001][2001];
vector<int> G[2001];
void init()
{
cin>>n;
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=n-1;i++){
int a;
cin>>a;
G[i+1].push_back(a);
G[a].push_back(i+1);
}
for(int i=1;i<=n;i++) {cin>>v[i];}
return;
}
long long work(int k,long long mod)
{
long long sum=0,d=1;
for(int j=1;j<=n;j++)
{
if(j!=1) d=(d*MAXN)%mod;
sum=(sum+d*A[k][j])%mod;
}
return sum;
}
void dfs(int st,int k,int nn,int num1[2001])
{
vv[k]=1;
if(num1[v[k]]==0){
nn++;
}
num1[v[k]]++;
A[st][k]=nn;
for(int i=0;i<G[k].size();i++){
int tt=G[k][i];
if(vv[tt]==0){
dfs(st,tt,nn,num1);
num1[v[tt]]--;
}
}
return;
}
int main()
{
cin>>t;
while(t--)
{
init();
for(int i=1;i<=n;i++) {
memset(num,0,sizeof(num));
memset(vv,0,sizeof(vv));
num[v[i]]++;
dfs(i,i,1,num);
}
for(int i=1;i<=n;i++)
{
cout<<work(i,MOD1)<<" "<<work(i,MOD2)<<endl;
}
}
return 0;
}
进阶题
1008题 Lawn of the Dead 死亡草坪
题目大意
给定一张
n
∗
m
n*m
n∗m 的地图,僵尸从
(
1
,
1
)
(1,1)
(1,1) 出发向右和向下走。地图上有
k
k
k 个地雷。求僵尸能经过的单元格总数。
考察内容
线段树
分析
考虑所有点的个数减去不能到达的点的个数,即为可以到达的点的个数。
根据题意,有地雷的地方是不可以到达的。由于僵尸只会向右和向下走,当某个点的左边和上方都不可达时,该点不可达,并会对自己右边的点和下方的点造成影响。
由于空间很大但地雷数有限,可以从上往下逐行对每一行的地雷排序后进行处理。对每个地雷,找到从自己的右上角点 ( x − 1 , y + 1 ) (x-1,y+1) (x−1,y+1) 开始的从左往右的连续不可达区域的范围,那么 x x x 这行的这个范围也不可达。可以用线段树来实现区间查询和区间覆盖。每一行处理完后查询该行不可达的点数,累加后用总点数减即得到答案。
#include<bits/stdc++.h>
using namespace std;
#define ls (x<<1)
#define rs (x<<1|1)
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;
vector<int>e[N];
int tr[2][N << 2], lz[2][N << 2];
void push_down(int f, int x, int l, int r, int mid) {
if (lz[f][x] == -1)return;
tr[f][ls] = lz[f][x] * (mid - l + 1);
tr[f][rs] = lz[f][x] * (r - mid);
lz[f][ls] = lz[f][rs] = lz[f][x];
lz[f][x] = -1;
}
void update(int f,int x, int l, int r, int L, int R, int v) {
if (L <= l && R >= r) {
tr[f][x] = (r - l + 1) * v;
lz[f][x] = v;
return;
}
int mid = (l + r) >> 1;
push_down(f, x, l, r, mid);
if (R <= mid)update(f, ls, l, mid, L, R, v);
else if (L > mid)update(f, rs, mid + 1, r, L, R, v);
else {
update(f, ls, l, mid, L, mid, v);
update(f, rs, mid + 1, r, mid + 1, R, v);
}
tr[f][x] = tr[f][ls] + tr[f][rs];
}
int query(int f, int x, int l, int r, int L, int R) {
if (!tr[f][x])return inf;
if (l == r)return l;
int mid = l + r >> 1;
push_down(f, x, l, r, mid);
if (L <= l && R >= r) {
if (tr[f][ls] > 0) return query(f, ls, l, mid, l, mid);
else return query(f, rs, mid + 1, r, mid + 1, r);
}
else {
if (R <= mid)return query(f, ls, l, mid, L, R);
else if (L > mid)return query(f, rs, mid + 1, r, L, R);
else return min(query(f, ls, l, mid, L, mid), query(f, rs, mid + 1, r, mid + 1, R));
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; ++i)e[i].clear();
for (int i = 1; i <= (m << 2); ++i) {
tr[0][i] = tr[1][i] = 0;
lz[0][i] = lz[1][i] = -1;
}
for (int i = 0; i < k; ++i) {
int x, y;
scanf("%d %d", &x, &y);
e[x].push_back(y);
}
long long ans = 0;
update(0, 1, 1, m, 1, 1, 1);
for (int x = 1; x <= n; ++x) {
int l = 0;
sort(e[x].begin(), e[x].end());
for (auto& y : e[x]) {
if (y - 1 >= l + 1) {
int pos = query((x & 1) ^ 1, 1, 1, m, l + 1, y - 1);
if (pos != inf)update(x & 1, 1, 1, m, pos, y - 1, 1);
}
l = y;
}
if (l + 1 <= m) {
int pos = query((x & 1) ^ 1, 1, 1, m, l + 1, m);
if (pos != inf)update(x & 1, 1, 1, m, pos, m, 1);
}
ans += tr[x & 1][1];
update((x & 1) ^ 1, 1, 1, m, 1, m, 0);
}
printf("%lldn", ans);
}
return 0;
}
1004题 Display Substring 显示子串
题目大意
给定一个字符串和每个字母的权重,输出权重第k小的连续子串。
样例输入
2
5 5
ababc
3 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
5 15
ababc
3 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
样例输出
4
-1
考察内容
后缀数据结构,二分答案
分析
二分答案,然后使用任意一种后缀数据结构 check 即可。
如何 check?
解一:后缀数组:所有后缀的所有前缀即所有子串。我们遍历后缀数组,对于每个后缀,其越长的前缀能
耗越大,于是可以二分找到能耗小于等于要 check 的值的前缀的个数,再减去重复部分即可。而每个后缀被重复统计的部分就是 height 数组对应的值。
解二:后缀自动机/后缀树
#include <bits/stdc++.h>
using namespace std;
const int N = 100000 + 9;
char s[N];
int n, c[26];
long long k;
template <int N, int sigma> struct Suffix_Automaton {
int tot, last, nxt[N * 2][sigma], len[N * 2], link[N * 2];
int sum[N], pos[N * 2];
vector<int> ch[N * 2];
void init(int n) {
tot = last = 0; link[0] = -1;
memset(nxt, 0, (n * 2 + 5) * sigma * sizeof(int));
memset(pos, 0, (n * 2 + 5) * sizeof(int));
for (int i = 0; i < n * 2 + 5; ++i) ch[i].clear();
}
void add_char(int c) {
int p = last, cur = last = ++tot; len[cur] = len[p] + 1;
while (~p && !nxt[p][c]) { nxt[p][c] = cur; p = link[p]; }
if (p == -1) { link[cur] = 0; return; }
int q = nxt[p][c];
if (len[q] == len[p] + 1) { link[cur] = q; return; }
int _q = ++tot; len[_q] = len[p] + 1;
memcpy(nxt[_q], nxt[q], sigma * sizeof(int));
link[_q] = link[q]; link[q] = link[cur] = _q;
while (~p && nxt[p][c] == q) { nxt[p][c] = _q; p = link[p]; }
}
void dfs(int u) {
for (int v : ch[u]) dfs(v);
if (!pos[u]) pos[u] = pos[ch[u][0]];
}
void add_string(const char s[], int n) {
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + c[s[i] - 'a'];
add_char(s[i] - 'a');
pos[last] = i;
}
for (int i = 1; i <= tot; ++i) {
ch[link[i]].push_back(i);
}
dfs(0);
}
bool check(int C) {
long long cnt = 0;
for (int i = 1; i <= tot; ++i) {
int P = pos[i], L = P - len[i] + 1, R = P - len[link[i]];
while (L < R) {
int M = (L + R) >> 1;
if (sum[P] - sum[M - 1] <= C) R = M;
else L = M + 1;
}
if (sum[P] - sum[L - 1] <= C) cnt += P - len[link[i]] - L + 1;
}
return cnt >= k;
}
};
Suffix_Automaton<N, 26> sam;
void solve() {
scanf("%d %lld", &n, &k);
scanf("%s", s + 1);
for (int i = 0; i < 26; ++i) {
scanf("%d", c + i);
}
sam.init(n);
sam.add_string(s, n);
int L = 1, R = sam.sum[n];
while (L < R) {
int M = (L + R) >> 1;
if (sam.check(M)) R = M;
else L = M + 1;
}
if (sam.check(L)) printf("%dn", L);
else printf("-1n");
}
int main() {
int T; scanf("%d", &T);
while (T--) solve();
return 0;
}
最后
以上就是舒服舞蹈为你收集整理的杭电第四场 题解签到题基本题进阶题的全部内容,希望文章能够帮你解决杭电第四场 题解签到题基本题进阶题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复