持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
补题连接
Dashboard - 2022 CCPC Henan Provincial Collegiate Programming Contest - Codeforces
比赛终榜
2022年河南省CCPC大学生程序设计竞赛 | RankLand (algoux.org)
qwq
我们在这里qwq!
赛后一直在后悔,因为H题的错误犯得太蠢了,如果先写 I 估计能出七题qaq
解题记录
A Mocha 上小班了
题目大意
输入n,输出最小的由n个互不相同的数字构成的不含前导零的正整数。
思路
签到题,如果n大于10,输出无解。
否则按照1023456789的顺序输出相应的位数。
代码
#include <bits/stdc++.h>
using namespace std;
int a[]={0,1,0,2,3,4,5,6,7,8,9};
signed main()
{
int n;
cin>>n;
if (n>10) cout<<-1<<endl;
else for (int i=1;i<=n;++i) cout<<a[i];
return 0;
}
E Serval 的俳句
题目大意
输入长度为n的仅由小写字母构成的字符串,找到它的一个长度为 17 的子序列,满足前5的字母一样,中间7个字母一样,最后5个字母一样。
思路
贪心地先从前到后找到5个相同字符,再继续遍历找到7个相同的字符,再继续遍历找5个相同字符,就可以找到一个正确结果。
正确性非常显然:
如原序列为“aaabbaabbb???????????????”(问号表示任意小写字母,我们并不关心),如果我们使用bbbbb作为第一句俳句可以求出合法解,则说明“???????????????”中一定存在“连续前7个字母一样,连续后5个字母一样”的子序列,那么aaaaa也是合法的第一句俳句。
若我们使用aaaaa作为第一句可以求出合法解,这说明“bbb???????????????”中一定存在“连续前7个字母一样,连续后5个字母一样”的子序列。当我们使用bbbbb作为第一句俳句时,若原序列为“aaabbaabbbbbbbqqqqqpoiuyt”则无法求出合法解了qaq
所以我们希望找到的每一句俳句都尽可能靠前结束。
代码
#include <bits/stdc++.h>
using namespace std;
int cnt[26],t,n;
char a[1000001];
char find(int qwq)
{
memset(cnt,0,sizeof(cnt));
while (t+1<=n)
{
t++;
cnt[a[t]-'a']++;
if (cnt[a[t]-'a']==qwq) return a[t];
}
printf("none\n");
exit(0);
}
signed main()
{
cin>>n;
cin>>a+1;
char ans1=find(5);
char ans2=find(7);
char ans3=find(5);
cout<<ans1<<ans1<<ans1<<ans1<<ans1;
cout<<ans2<<ans2<<ans2<<ans2<<ans2<<ans2<<ans2;
cout<<ans3<<ans3<<ans3<<ans3<<ans3;
return 0;
}
F 集合之和
题目大意
定义两个含有有限个元素的整数不可重集 的加法运算:
给定n,构造数集满足 ,且 中元素不超过500000。
思路
赛中一直在想含有 x 个元素的 A 能构造出的 |A+A| 范围是多少,怎样才能全都取到,浪费了很长时间…………队友一想省赛签到题不可能这么麻烦拍板换思路,手玩了一波发现直接输出 就可以构造出n为奇数的情况,然后最后一位加一就是偶数的情况。我上机写完立马过了。
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
if (n==2||n==4) return printf("-1\n"),0;
if (n&1)
{
printf("%d\n",n+1>>1);
for (int i=1;i<=(n+1)/2;i++) printf("%d ",i);
return 0;
}
printf("%d\n",n>>1);
for (int i=1;i<(n>>1);i++) printf("%d ",i);
printf("%d",(n>>1)+1);
return 0;
}
G Mocha 上大班啦
题目大意
骗子题,一开始想概率怎么处理,直接敲了一堆逆元什么的,定睛一看其实是输出 n 个数字串按位与运算后得到的数字串中 1 的个数。
数组开小wa了一发,锅是我的QAQ
思路
模拟。
代码
#include <bits/stdc++.h>
using namespace std;
int a[4001],n,m,x,ans;
int main()
{
scanf("%d%d",&n,&m);
for (int j=1;j<=m;++j) a[j]=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
scanf("%1d",&x);
a[j]&=x;
}
for (int j=1;j<=m;++j) ans+=a[j];
printf("%d\n",ans);
return 0;
}
H 旋转水管
题目大意
整个游戏由 4 × m 的矩形构成。
第一行仅包含一个位于第 x 列的水流输入口,水流自输入口向下流出。
游戏第二、三行有 m 列水管,第 i 行 j 列为 I 或者 L 表示水管的形状。
第四行仅包含一个位于第 y 列的水流输出口,水流输出口仅接受向下流出的水流。
给出 4 × m 的矩形,判断能否通过旋转水管使得水流输入口和水流输出口相通。
思路
队友想写爆搜,我想写分类大讨论,于是开始抢键盘(bushi
我写了一发分类讨论wa了,队友写了一发爆搜没过样例,我俩都想调试代码,我灌输了一波我的思路:
(1)如果 ,有以下三种输出 YES 情况:
直接循环判断即可。
(2)如果 ,如果 ,可以对地图进行翻转,使得 。所以只考虑入水口在出水口左边的情况。
入水口先往左乱走一波再向右到 x+1 列有4种可能的情况:
出水口也可以先向右乱走一波再向左到 y-1 列,情况与之相似。我们只需判断从 x+1 到 y-1 列的水流情况。
记录 x1 为 1 表示第一行能出水,为 0 表示不能。x2 表示第二行的情况,与 x1 相同。从 x+1 到 y-1 循环判断,如果当前列是两根 L 管,则交换 x1 和 x2,否则是 L 管的行赋值为 0。运算结束后如果 1=x1=y1 或者 1=x2=y2 即可输出 YES。
之后大家都感觉我的思路很正确,于是全队开始读我的代码,队友跑了一大堆智慧数据,都和手玩的一致……
最后发现问题在于当 时,地图翻转错了……好在最终比赛结束前20min过了,成功拿金。
代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100001;
// const long long mod=998244353;
int m,x,y;
int a[2][N];
int solve()
{
scanf("%d%d%d",&m,&x,&y);
char ch;
for (int i=1;i<=m;++i) cin>>ch,a[0][i]=(ch=='I');
for (int i=1;i<=m;++i) cin>>ch,a[1][i]=(ch=='I');
//1
if (x==y)
{
if (a[0][x]&&a[1][x]) return 1;
if (a[0][x]||a[1][x]) return 0;
for (int i=x-1;i>0;--i)
{
if (a[0][i]&&a[1][i]) continue;
if (a[0][i]||a[1][i]) break;
return 1;
}
for (int i=x+1;i<=m;++i)
{
if (a[0][i]&&a[1][i]) continue;
if (a[0][i]||a[1][i]) break;
return 1;
}
return 0;
}
//x<y
if (x>y)
{
for (int i=1,j=m;i<j;i++,j--)
{
swap(a[0][i],a[0][j]);
swap(a[1][i],a[1][j]);
}
x=m-x+1;
y=m-y+1;
}
//0~x-1 y+1~n
int xu,xd,yu,yd;
if (a[0][x]&&a[1][x]) return 0;
if (a[0][y]&&a[1][y]) return 0;
if (a[0][x]) xu=0,xd=1;
else
{
xu=1;
xd=0;
if (a[1][x])
for (int i=x-1;i>0;--i)
{
if (a[0][i]&&a[1][i]) continue;
if (a[0][i]||a[1][i]) break;
xd=1;
break;
}
}
if (a[1][y]) yd=0,yu=1;
else
{
yd=1;
yu=0;
if (a[0][y])
for (int i=y+1;i<=m;++i)
{
if (a[0][i]&&a[1][i]) continue;
if (a[0][i]||a[1][i]) break;
yu=1;
break;
}
}
for (int i=x+1;i<=y-1;++i)
if (a[0][i]==0&&a[1][i]==0) swap(xu,xd);
else xu&=a[0][i],xd&=a[1][i];
return (xu&&yu)||(xd&&yd);
}
int main()
{
int T;
for (scanf("%d",&T);T--;)
if (solve()) printf("YES\n");
else printf("NO\n");
return 0;
}
J Mex Tree
题目大意
mex(S) 表示不属于 S 的最小非负整数。
给定一棵 个点的树,编号为 ,第 i 个点的权值为 是 0 到 n−1 的一个排列。 对于 k 从 0 到 n,求这棵树的最大非空连通子图,其点集 S 满足 恰好为 k。
思路
看到题口胡立马有思路了但是感觉不太好写,给队友口胡了一波队友表现的非常茫然,表示有思路就先写一会儿吧。被赶上机飞速写了一发过了(x
求以权值为 u 的点为根的子树中有多少个点权值小于 u,记为 cnt[u]。可以转化为经典的二维数点问题求解。
对于每个权值为 u 的点:
- 若 cnt[u] 为 0,说明以 u 为根节点的子树中不包含比 u 小的点,也就是说整棵树删去以 u 为根节点的子树,剩下的树中包括所有权值小于 u 的点。输出总点数减子树大小即可。
- 否则遍历权值为 u 的点的所有子节点,若其中有以子节点为根的子树中包含恰好 u 个权值小于 u 的点,则输出该子树的大小。
代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <vector>
#define lowbit(t) (t&-t)
using namespace std;
const int N=1000001;
// const long long mod=998244353;
struct asdf{
int id,val,siz;
}q[N],qq[N];
int n,idx,val[N],s[N];
vector<int>e[N];
void dfs(int u)
{
q[u].siz=1;
q[u].val=u;
q[u].id=++idx;
for (auto v:e[u])
{
if (q[v].id) continue;
dfs(v);
q[u].siz+=q[v].siz;
}
}
int cmp(asdf a,asdf b)
{
return a.val<b.val;
}
void add(int t)
{
for (;t<=n;t+=lowbit(t))
s[t]++;
}
int getcnt(int t)
{
int cnt=0;
for (;t;t-=lowbit(t))
cnt+=s[t];
return cnt;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&val[i]);
//为了便于处理,直接以权值为点的标号建图
for (int i=2,x;i<=n;++i)
{
scanf("%d",&x);
e[val[x]].push_back(val[i]);
e[val[i]].push_back(val[x]);
}
dfs(0);
for (int i=0;i<n;++i) qq[i]=q[i];
sort(q,q+n,cmp);
for (int i=0,x,ans;i<n;++i)
{
x=getcnt(q[i].id+q[i].siz-1)-getcnt(q[i].id-1);
if (x==0&&i!=0) ans=n-q[i].siz;
else
{
ans=0;
for (auto v:e[q[i].val])
{
x=getcnt(qq[v].id+qq[v].siz-1)-getcnt(qq[i].id-1);
if (x==i) ans=max(ans,qq[v].siz);
}
}
printf("%d ",ans);
add(q[i].id);
}
printf("%d\n",n);
}