
UNIQUE VISION Programming Contest 2025 Spring (AtCoder Beginner Contest 398) D-E 题解
由于很多低级错误(写成了),D RE了2发,E WA了5发F马上补。
由于很多低级错误(*
写成了 +
),D RE了2发,E WA了5发
D. Bonfire
题意
在一个足够大的平面上,有一堆篝火
在时间 t = 0 t=0 t=0 时,仅在单元格 ( 0 , 0 ) (0,0) (0,0) 有一团烟
给你 N , R , C N,R,C N,R,C 和字符串 S S S,对于时间 t = 1 , 2 , . . . , N t=1,2,\ ...,\ N t=1,2, ..., N,按顺序 执行以下操作:
-
对于所有存在烟的单元格 ( x , y ) (x,y) (x,y):
如果 S t S_t St =
N
,则单元格 ( x , y ) (x,y) (x,y) 中烟移动到单元格 ( x − 1 , y ) (x-1,y) (x−1,y)。同理,
W
: ( x , y − 1 ) (x,y-1) (x,y−1)S
: ( x + 1 , y ) (x+1,y) (x+1,y)E
: ( x , y + 1 ) (x,y+1) (x,y+1) -
如果此时单元格 ( 0 , 0 ) (0,0) (0,0) 没有烟,就在单元格 ( 0 , 0 ) (0,0) (0,0) 再生成一团烟
-
判断此时单元格 ( R , C ) (R,C) (R,C) 是否有烟,若有,输出
1
,否则输出0
(不要换行)
思路
用二维
set
\text{set}
set 存储单元格信息:set<int> st[1000005]
第一维代表行,第二维(单个 set \text{set} set)代表列,整体表示当前行所包含的所有的有烟的单元格列号
由于要整体移动,复杂度极高,我们直接考虑将行号和列号移动:
定义变量 x , y x,y x,y 使得坐标 ( p , q ) (p,q) (p,q) 表示实际单元格 ( p + x , q + y ) (p+x,q+y) (p+x,q+y) ,即单元格 ( i , j ) (i,j) (i,j) 对应坐标为 ( i − x , j − y ) (i-x,j-y) (i−x,j−y)
对于每个
1
≤
t
≤
N
1\le t\le N
1≤t≤N,具体操作如下
N
:
x
=
x
+
1
S
:
x
=
x
−
1
E
:
y
=
y
+
1
W
:
y
=
y
−
1
\text{N}:x=x+1 \\ \text{S}:x=x-1 \\ \text{E}:y=y+1 \\ \text{W}:y=y-1
N:x=x+1S:x=x−1E:y=y+1W:y=y−1
接下来判断单元格
(
0
,
0
)
(0,0)
(0,0) 是否有烟,即 st[0-x].find(0-y)!=st.end()
是否成立,若成立,无事发生;否则 st[0-x].insert(0-y)
最后判断单元格
(
R
,
C
)
(R,C)
(R,C) 是否有烟,即 st[R-x].find(C-y)!=st.end()
是否成立,若成立,输出 1
;否则输出 0
注意对应坐标可能为负数,注意下标不要越界
C++ 代码
#include<bits/stdc++.h>
using namespace std;
set<int> st[1000005];//此处我RE了2次,就是因为数组开的太小,单元格(R,C)所对应的坐标可能小到-4e5
int n,r,c;
string s;
int main(){
cin>>n>>r>>c>>s;
int x=-500000,y=0;
st[-x].insert(0);//为方便理解,下文-a统一写成0-a
for(int i=0;i<n;i++){
//步骤1:整体移动烟的位置
if(s[i]=='N') x--;
else if(s[i]=='S') x++;
else if(s[i]=='W') y--;
else y++;
//步骤2:判断单元格(0,0)是否有烟
if(st[0-x].find(0-y)==st[0-x].end()) st[0-x].insert(0-y);
//步骤3:判断单元格(R,C)是否有烟
if(st[r-x].find(c-y)==st[r-xd].end()) cout<<0;
else cout<<1;
}
return 0;
}
E. Tree Game
题意
本题为交互题,请使用 endl
作为换行符(这是最方便的刷新方式)
给你一棵 N N N 个节点的树, U i U_i Ui 与 V i V_i Vi 之间有一条双向边
你现在要玩一个游戏,可以 自行选择先手/后手,双方轮流进行一下操作:
- 选择两个点 i , j i,j i,j,要求 1 ≤ i < j ≤ N 1\le i<j\le N 1≤i<j≤N, ( i , j ) (i,j) (i,j) 之间尚未连边,在 ( i , j ) (i,j) (i,j) 之间连一条边
- 连边之后,不可出现 奇环
- 若无法执行上述操作,这个人就输了
你现在和交互机轮流操作,你必须得赢(保证可以实现),给出方案,格式如下:
- 首先输出
First
(先手)或Second
(后手) - 再轮流输出 i i i 和 j j j
- 最后,若对方输出 ( − 1 , − 1 ) (-1,-1) (−1,−1) 表示你已经赢了
思路
显然,树是一个二分图,可以先染色,将节点分为集合 A A A 和集合 B B B,显然 ∣ A ∣ + ∣ B ∣ = N |A|+|B|=N ∣A∣+∣B∣=N,其中 ∣ S ∣ |S| ∣S∣ 表示集合 S S S 中元素的个数
将 A A A 与 B B B 中的点两两连边,显然这个图还是二分图,不会出现奇环,可得共有连 ∣ A ∣ × ∣ B ∣ |A| \times |B| ∣A∣×∣B∣ 条边
由于树中已经有 N − 1 N-1 N−1 条边,所以一共可以再连 ∣ A ∣ × ∣ B ∣ − ( N − 1 ) |A| \times |B|-(N-1) ∣A∣×∣B∣−(N−1) 条边,所以操作轮数是 固定的!
设操作轮数为 k k k,若 k k k 为奇数,则先手必胜;否则后手必胜
把所有可连的边用 set<pair<int,int> >
存储,一定要注意
i
<
j
i<j
i<j,因为我就是这么WA了5发,每次取 set 中任意一条边输出(建议第一个或最后一个)并删除,随后删除对方所选边,直到第
⌊
k
2
⌋
\displaystyle \lfloor\frac{k}{2} \rfloor
⌊2k⌋ 次操作结束
C++ 代码
#include<bits/stdc++.h>
#define mpr make_pair
#define sz(v) (int)v.size()
using namespace std;
const int maxn=105;
int n;
vector<int> g[maxn];
int col[maxn];
bool e[maxn][maxn];
bool used[maxn];
void dfs(int v,int p){
used[v]=true;
col[v]=p;
for(int x:g[v]){
if(!used[x]){
dfs(x,p^1);
}
}
}
int main(){
//读入并连边
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].pb(v);
g[v].pb(u);
e[u][v]=e[v][u]=1;
}
//染色并分割
dfs(1,0);
vector<int> a,b;
for(int i=1;i<=n;i++){
if(col[i]==0) a.pb(i);
else b.pb(i);
}
//计数并统计可连边
int num=sz(a)*sz(b)-(n-1);
set<pair<int,int> > st;
for(int x:a){
for(int y:b){
if(!e[x][y]){//注意i<j
if(x<y) st.insert(mpr(x,y));
else st.insert(mpr(y,x));
}
}
}
//开始交互并输出
if(num%2==1){//先手必胜状态
cout<<"First"<<endl;
cout<<st.begin()->first<<" "<<st.begin()->second<<endl;
st.erase(st.begin());
for(int i=0;i<num/2;i++){
int p,q;
cin>>p>>q;
st.erase(mpr(p,q));
cout<<st.begin()->first<<" "<<st.begin()->second<<endl;
st.erase(st.begin());
}
}else{//后手必胜状态
cout<<"Second"<<endl;
for(int i=0;i<num/2;i++){
int p,q;
cin>>p>>q;
st.erase(mpr(p,q));
cout<<st.begin()->first<<" "<<st.begin()->second<<endl;
st.erase(st.begin());
}
}
return 0;
}
更多推荐
所有评论(0)