A.疾羽的救赎
思路:这道题的思路其实非常清晰,题意也很好懂,如果说有个关键点的话就是把每个棋子当前的位置做一下标记,我这里设置的是now数组,然后用一个deque数组当作棋盘,每一个元素上面都可以摞棋子(因为可以双端出入),这里的话,主要是考虑到进出的顺序,从尾出从头进,就可以不乱顺序。主体就是通过now找到某个棋子的位置,通过位置定位到棋盘上的某个格子找到该双端队列的元素,取出,然后放到相应的位置(注意如果之前有棋子的话要摞到之前棋子的上面)。
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--){
int cnt = 12;
int a,b;
int now[4] = {0,2,3,4};//下标表示颜色,值用来表示某个颜色的棋子的当前位置
deque<int> v[20];//表示棋盘
for(int i = 1; i <= 3; i++)
v[now[i]].push_back(i);
while(cnt --)
{
cin>>a>>b;
int flag = 0;
deque<int> &t = v[now[a]];
deque<int> u;
while(t.back() != a)
u.push_front(t.back()), now[t.back()] +=b, t.pop_back();
u.push_front(t.back()),now[t.back()] +=b, t.pop_back();
while(!v[now[a]].empty())
{
u.push_front(v[now[a]].back());
v[now[a]].pop_back();
}
v[now[a]]=u;
}
if(v[9].size() == 3) cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
return 0;
}
B. 终焉之茧
这道题就是一个交互题,二分求点,这里参考了大佬的答案
2023CCPC女生赛 女生专场题解 ABFGHKL(更新至7题)-优快云博客
主要就是读懂题意,然后是题目每次评判给的都是你给的要传送的距离,并不是坐标,然后就是比较左右两边哪一边离终点比较近,然后找到最近的,x轴和y轴分别进行二分查找就可以了。因为2的30次方很大,所以基本不会超过。
#include "iostream"
using namespace std;
typedef long long ll;
ll l = -1000, r = 1000;
ll nowx, nowy;
ll ask(ll x, ll y){
cout<<x - nowx<< ' '<< y - nowy<<endl;
nowx = x, nowy = y;
fflush(stdout);
ll ans;
cin>>ans;
return ans;
}
int main(){
ll d, mid;
cin>>d;
if(d == 0) return 0;
ll d1 = ask(l,0);
if(d1 == 0) return 0;
ll d2 = ask(r,0);
if(d2 == 0) return 0;
while(l < r){
if(d1 < d2){
mid = l + r >> 1;
d2 = ask(mid, nowy);
if(d2 == 0) return 0;
r = mid;
}
else if(d1 > d2){
mid = l + r + 1>> 1;
d1 = ask(mid, nowy);
if(d1 == 0) return 0;
l = mid;
}
else{
mid = l + r >> 1;
if(ask(mid, nowy) == 0 ) return 0;
break;
}
}
l = -1000, r = 1000;
d1 = ask(nowx, l);
if(d1 == 0) return 0;
d2 = ask(nowx, r);
if(d2 == 0) return 0;
while(l < r){
if(d1 < d2){
mid = l + r >> 1;
d2 = ask(nowx, mid);
if(d2 == 0) return 0;
r = mid;
}
else if(d1 > d2){
mid = l + r + 1>> 1;
d1 = ask(nowx, mid);
if(d1 == 0) return 0;
l = mid;
}
else{
mid = l + r >> 1;
if(ask(nowx,mid) == 0 ) return 0;
break;
}
}
return 0;
}
F. 最长上升子序列
实际上在赛场上做这题的时候我们甚至都还不知道什么叫拓扑排序,之后上数据结构课才知道,当成一个思维题来做的,当时离结束就半小时左右了吧做的这道题,我用了两分钟就写完了,其实主题思路就是从最小的那个递增子序列长度从一开始排列,碰到一样长度的就从后往前排列(这样可以保证递增序列长度不变),然后当递增的序列断档也就是不连续的时候该序列不合法,很容易明白的道理。见下图:
#include "iostream"
#include "vector"
using namespace std;
const int N = 1e7;
vector<int > v[N];
int a[N], b[N];
int main(){
int n;
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int max = 0, flag = 0;
cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i];
if(a[i] > max)
{
if(a[i] - max > 1) flag = 1;
max = a[i];
}
v[a[i]].push_back(i);
}
if(flag) cout<<"-1"<<endl;
else
{
int p = 1;
for(int i = 1; i <= max; i++)
for(int j = v[i].size() - 1; j >= 0; j --)
{
int t = v[i][j];
b[t] = p ++;
}
for(int i = 1; i <= n; i++)
cout<<b[i]<<' ';
cout<<endl;
}
return 0;
}
G. 精灵宝可梦对战
就是一个大模拟,具体的见我之前的博客,
ps:直接提交cf的话把注释删一下,否则会提示不是简单文本~~
#include "iostream"
#include "queue"
#include "vector"
#include "algorithm"
using namespace std;
struct BK{
int H,A,B,C,D,E,W,e;
};//属性比较多,所以考虑用结构体,一个BK代表一个宝可梦
int h,a,b,c,d,e,w,ee;
queue<BK> AA,BB; //AA和BB 表示小A和小B ps:比较形象
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); //关流很重要,否则会超时
int n,m,k,cnt1 = 0, cnt2 = 0;
cin>>n>>m>>k;
while(n--){
cin>>h>>a>>b>>c>>d>>e>>w;
BK x ;
x.H = h, x.A = a, x.B = b, x.C = c, x.D = d, x.E = e, x.W = w, x.e = 0;
AA.push(x);
} //把小A的宝可梦载入
while(m--){
cin>>h>>a>>b>>c>>d>>e>>w;
BK x;
x.H = h, x.A = a, x.B = b, x.C = c, x.D = d, x.E = e, x.W = w, x.e= 0;
BB.push(x);
} //把小B的宝可梦载入
BK aa = AA.front();
BK bb = BB.front();
k *= 2;
long long count = 0; //aa是攻击方,bb是被攻击方
while(!AA.empty() && !BB.empty() && k > 0){ //攻击过程,结束条件是某一个人的宝可梦死光或者局数用完
long long hit, p, q;
p = aa.A - bb.C > 0? aa.A - bb.C :0;
q = aa.B - bb.D > 0? aa.B - bb.D :0;
hit = max(p, q);
if(aa.W > hit && aa.e >= aa.E) hit = aa.W, aa.e -= aa.E;
if(hit == p || hit == q) aa.e ++; //模拟攻击,找伤害,一定注意是实际伤害最大的,别忘了能量的变化
bb.H -= hit;
AA.push(aa);
AA.pop();
aa = AA.front(); //攻击方换发
if(bb.H <= 0)
{
BB.pop();
bb = BB.front();//被攻击方死了才能换下一个
}
swap(aa,bb);
swap(AA,BB); //A和B换发
count ++;
k--;
}
if(k >= 0 && !AA.empty() && !BB.empty()) cout<<"Draw"<<endl; //没死光就是平局
else if(count % 2 == 0) cout<<"Bob"<<endl; //总是A是单数攻击,B是双数攻击,所以看结束的时候的攻击局数就可以判断谁赢了
else if(count % 2 == 1) cout<<"Alice"<<endl;
return 0;
}
H. 字符串游戏
就是一个AC自动机的板子,具体看我之前的博客
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include "map"
using namespace std;
typedef long long ll;
const int maxn = 2*1e6+9;
long long xx = 0;
long long len;
int trie[maxn][26]; //字典树
int cntword[maxn]; //记录该单词出现次数
int fail[maxn]; //失败时的回溯指针
int cnt = 0;
long long modd = 1e9 + 7;
map<int, int> mp;
void insertWords(string s){
int root = 0;
for(int i=0;i<s.size();i++){
int next = s[i] - 'a';
if(!trie[root][next])
trie[root][next] = ++cnt, mp[cnt] = i + 1;
root = trie[root][next];
}
cntword[root]++; //当前节点单词数+1
}
void getFail(){
queue <int>q;
for(int i=0;i<26;i++){ //将第二层所有出现了的字母扔进队列
if(trie[0][i]){
fail[trie[0][i]] = 0;
q.push(trie[0][i]);
}
}
//fail[now] ->当前节点now的失败指针指向的地方
//tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
while(!q.empty()){
int now = q.front();
q.pop();
for(int i=0;i<26;i++){ //查询26个字母
if(trie[now][i]){
//如果有这个子节点为字母i+'a',则
//让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
//有点绕,为了方便理解特意加了括号
fail[trie[now][i]] = trie[fail[now]][i];
q.push(trie[now][i]);
}
else//否则就让当前节点的这个子节点
//指向当前节点fail指针的这个子节点
trie[now][i] = trie[fail[now]][i];
}
}
}
void query(string s){
int now = 0,ans = 0;
for(int i=0;i<s.size();i++){ //遍历文本串
now = trie[now][s[i]-'a']; //从s[i]点开始寻找
for(int j=now;j ;j=fail[j]){
//一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
ans += cntword[j];
if(cntword[j] > 0) xx =(xx + (i - mp[j] + 2) * (len - i) ) % modd;
//cntword[j] = -1; //将遍历国后的节点标记,防止重复计算
}
}
// return ans;
}
int main() {
int n, m;
string s;
cin >> n >> m;
for(int i=0;i<n;i++){
cin >> s ;
insertWords(s);
}
fail[0] = 0;
getFail();
while(m --){
xx = 0;
cin >> s ;
len = s.length() ;
query(s) ;
cout<<xx<<endl;
}
return 0;
}
K. RSP
水题 1/n,别问,问就是水。
#include "iostream"
using namespace std;
int main(){
long long n,m;
cin>>n>>m;
cout<<"1/"<<n<<endl;
return 0;
}
L. 养成游戏
这道题是月月在赛场上做出来的,思维清晰,手忙脚乱就是她(略略略)
以下是月月的原话:
“ 就是,就是暴力嘛,每一种属性都有0到K种选择,求出得分最高的一套选择就一个个试,试n的K次方次就可以了。最难是6的8次方,所以可以暴力;
dfs从第一个属性开始从0—K赋值,接着第二个属性…一直递归到第K个属性,证明赋完一套值了,就check一下这套属性的最终得分;将每一套方案都计算完了,maxx里面存的也就是最高的得分了。需要注意的是,评委的评分可能不小,得开longlong,以及每套属性评分时都是固定的,比如这个评委用到的A3是x,另一个评委用到的A3也要是x,不能随便更改
”
#include<bits/stdc++.h>
#define N 1000000
#define int long long
using namespace std;
int I[N],J[N],op[N],a[N],b[N],d[N],v[N],A[N];
int n,m,K;
int maxx=0;
int check(){
int sum=0;
for(int i=0;i<m;i++){
if(op[i]==0&&a[i]*A[I[i]]+b[i]*A[J[i]]<=d[i]) sum+=v[i];
if(op[i]==1&&a[i]*A[I[i]]+b[i]*A[J[i]]>=d[i]) sum+=v[i];
}
return sum;
}
void dfs(int x,int y){
A[x]=y;
if(x==n){
maxx=max(maxx,check());
return ;
}
for(int i=0;i<=K;i++){
dfs(x+1,i);
}
return ;
}
main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m>>K;
for(int i=0;i<m;i++){
cin>>I[i]>>J[i]>>op[i]>>a[i]>>b[i]>>d[i]>>v[i];
}
for(int i=0;i<=K;i++){
dfs(1,i);
}
cout<<maxx<<endl;
}
总结:
这次是抱着拿奖的心态去了,也因为是第一次参加这么大型的比赛,敲代码手都不利索了,还好队长花花和月月临危不乱,斩下四题,充满遗憾,就差一点就拿奖了,下次继续努力吖!!!