2024.8.5 12:00————17:00
CCPC 2024, Shanghai
- [A - 无线网络整点栅格统计](https://atcoder.jp/contests/math-and-algorithm/tasks/abc204_d?lang=en)
- [E - 无线软件日](https://atcoder.jp/contests/abc265/tasks/abc265_a?lang=en)
- [J - 极简合数序列](https://atcoder.jp/contests/abc265/tasks/abc265_a?lang=en)
- [L - 扩散模型](https://atcoder.jp/contests/abc265/tasks/abc265_a?lang=en)
- [M - 不共戴天](https://atcoder.jp/contests/abc265/tasks/abc265_a?lang=en)
A - 无线网络整点栅格统计
偷了廷哥的代码,当时写了一半发现他的想法更优化,我就直接撤了。
…?他跟我一个写法!还跟我抢电脑!拱开!我都写了一半了他来了就让他了!
题解:
有一个nm的规划区域,求以每个端点为正方形的顶点,可以规划出多少个不同的面积为正的正方形,最后输出一个(n+1)(m+1)的矩阵。
直接枚举每个点和对角顶点,注意不要加上自己,可以算出剩下俩个顶点的位置,然后判断它们是否在区域内。
代码:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cmath>
#include<utility>
#include<map>
#include<vector>
#include<deque>
using namespace std;
#define int long long
int n,m;
int arr[101][101];
signed main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
for(int x=0;x<=n;x++){
for(int y=0;y<=m;y++){
int x1=i,y1=j,x2=x,y2=y;
int cha1 = x2-x1, cha2 = y2-y1;
int x3=x1+cha2, y3=y1-cha1;
int x4=x3+cha1, y4=y3+cha2;
if(x3>=0 && x3<=n && y3>=0 && y3<=m && x4>=0 && x4<=n && y4>=0 && y4<=m) arr[i][j]++;
}
}
}
}
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++) cout<<arr[i][j]-1<<" ";
cout<<'\n';
}
return 0;
}
E - 无线软件日
题解:
给出字母个数n,和字符串S,可以把每个字母剪下来,求最后可以拼出多少个“shanghai”,大小字母不敏感。
数一下对应字母分别有几个,以小的输出即可。
代码:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cmath>
#include<utility>
#include<map>
#include<vector>
#include<deque>
using namespace std;
int n;
string s;
map<char,int>mp;
signed main() {
cin >> n;
cin >> s;
for (int i = 0; i <= n-1; i++) {
if(s[i] == 's' || s[i] == 'S') mp['s']++;
if(s[i] == 'h' || s[i] == 'H') mp['h']++;
if(s[i] == 'a' || s[i] == 'A') mp['a']++;
if(s[i] == 'n' || s[i] == 'N') mp['n']++;
if(s[i] == 'g' || s[i] == 'G') mp['g']++;
if(s[i] == 'i' || s[i] == 'I') mp['i']++;
}
mp['h']=mp['h']/2;
mp['a']=mp['a']/2;
int ans = min(mp['a'],mp['h']);
ans = min(ans,mp['s']);
ans = min(ans,mp['n']);
ans = min(ans,mp['i']);
ans = min(ans,mp['g']);
cout << ans << endl;
return 0;
}
J - 极简合数序列
题解:
给出t组数据,每组包含n个整数,求最少几个连续素数加起来是非素数,减去1。
前缀和维护,直接枚举1,2,3,4的子区间,如果有加起来是非素数的,就输出它的区间差,否则输出-1。
代码:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cmath>
#include<utility>
#include<map>
#include<vector>
#include<deque>
using namespace std;
#define int long long
int t;
int a[1005];
int qz[1005];
bool st[10040];
signed main() {
cin >> t;
for (int i = 1; i <= 10000; i++) {
for (int j = 2; j*j <= i; j++) {
if(i % j == 0){
st[i] = true;
break;
}
}
}st[2] = false;
while(t--) {
int n;
cin >> n;
bool stt = true;
for (int i = 1; i <= n; i++) {
cin >> a[i];
qz[i] = qz[i-1]+a[i];
if(st[a[i]])stt = false;
}
if(n == 1) {
if(st[a[1]]) {
cout << 0 << endl;
}else cout << -1 << endl;
stt = false;
continue;
}
else if(!stt) {
cout << 0 << endl;
continue;
}
else {
for (int i = 1; i <= n-1; i++) {
if(st[qz[i+1]-qz[i-1]] && stt) {
cout << 1 << endl;
stt = false;
break;
}
}
for (int i = 1; i <= n-2; i++) {
if(st[qz[i+2]-qz[i-1]] && stt) {
cout << 2 << endl;
stt = false;
break;
}
}
for (int i = 1; i <= n-3; i++) {
if(st[qz[i+3]-qz[i-1]] && stt) {
cout << 3 << endl;
stt = false;
break;
}
}
if(stt) cout << -1 << endl;
}
}
return 0;
}
L - 扩散模型
傅姐太牛啦啦啦啦,这题的正解应该是树形DP,没想到她这样就做出来啦!赛后看了榜单,正式比赛都能拿银啦!全靠这一题。
题解:
题意太复杂了我都有点懒得说。大概就是一棵有n个节点的树,现在你需要走到K
个被标记叶子结点之一,在已经给出的M个候选词中最少选择几个可以走到,如果走不到输出-1。
具体见注释。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
#define PII pair<int,int>
signed main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
int n,m,k;
cin>>n>>m>>k;
bool g[n+10];
//标记是否走过
int s[n+10];
//走到当前点最少需要使用几次魔法
int p[n+10];
//标记能否走到这个点
int f[n+10];
//记录父亲节点
memset(f,0,sizeof f);
memset(g,0,sizeof g);
memset(s,0,sizeof s);
memset(p,0,sizeof p);
map<PII,int>mp;
vector<int>a[n+10];
queue<int>q;
for(int i=1;i<=n;i++){
int l;
cin>>l;
if(l==0) {
q.push(i);
continue;
}
while(l--){
int h;
cin>>h;
f[h]=i;
a[i].push_back(h);
}
}
while(m--){
int x,y;
cin>>x>>y;
mp[{x,y}]=1;
mp[{y,x}]=1;
}
for(int i=0;i<k;i++) {
int x;
cin>>x;
p[x]=1;
}
while(q.size()){
int x=q.front();
q.pop();
if(g[x]) continue;
g[x]=1;
int sum=0;
int ksum=0;
int sum1=0;
int now=N*10;
for(int i=0;i<a[f[x]].size();i++){
//它的父亲节点的所有子节点
g[a[f[x]][i]]=1;
if(p[a[f[x]][i]]){
//有标记的叶子结点
sum++;
ksum+=s[a[f[x]][i]];
if(mp.count({f[x],a[f[x]][i]})!=0){
//可以使用魔法
sum1++;
now=min(now,s[a[f[x]][i]]);
}
}
}
if(sum==a[f[x]].size()) {
if(sum!=0){
p[f[x]] = 1, s[f[x]] = min(ksum,now+1);
//这个地方很有意思,可以想象一下,下面三个都是一样的次数,那么走哪个都可以,不要用魔法
}
else{
p[f[x]] = 1, s[f[x]] = ksum;
}
}
else {
if(sum1>0) {
p[f[x]]=1;
s[f[x]]=now+1;
}
else p[f[x]]=0;
}
q.push(f[x]);
//继续往上找
}
if(p[1]) cout<<s[1]<<endl;
//能找到1节点
else cout<<-1<<endl;
}
return 0;
}
M - 不共戴天
声明一下这题也不是我写的,我只是参与思维讨论,所以这难听的变量名跟我一毛钱关系都没有,爬爬爬
题解:
n片荷叶,求俩只动物的捕食策略,使得并不会出现二者从同一起点跳到同一终点的情况。
可以想像成n个格子,一个一直横着走,一个一直竖着走,取较小的那个就是策略,尽量n个格子的排序趋于正方形,有多的一个格子直接放出即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int t = sqrt(n);
if(n-t*t>t) t++;
int arr[t+105][t+105];
memset(arr,0,sizeof arr);
int cnt = 0;
for(int i=1;i<=t+1;i++){
for(int j=1;j<=t;j++) {
arr[i][j] = ++cnt;
if (cnt == n) goto aa;
}
}
aa: ;
vector<pair<int,int> > gua,niao;
for(int i=1;i<=t+1;i++){
for(int j=1;j<=t;j++){
if(arr[i][j+1]) gua.push_back({arr[i][j],arr[i][j+1]});
if(arr[i+1][j]) niao.push_back({arr[i][j],arr[i+1][j]});
}
}
int cnm = min(gua.size(),niao.size());
cout<<cnm<<endl;
for(int i=0;i<cnm;i++) cout<<gua[i].first<<" "<<gua[i].second<<'\n';
for(int i=0;i<cnm;i++) cout<<niao[i].first<<" "<<niao[i].second<<'\n';
return 0;
}