D – Collision
题目描述:
给一颗树,
q
次询问,每次询问给两个点x
和y
,两个点上放两个人以相同的速度,沿着二者的最短路前进,问二者会在点上相遇,还是在边上相遇
思路:
不难发现,对于任意两个点
x
和y
,如果二者在树上的最短距离是奇数,则在边上相遇,否则在点上相遇树上最短距离可以用
lca
求,但是这里没必要这么求,我们只需要知道二者距离的奇偶性即可,那我们可以对整个图进行黑白染色,如果颜色相同,则是在点上相遇,否则是在边上相遇
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 300000 + 50
int n, m, k, x;
vector<int>G[MAX];
int col[MAX];
void dfs(int u, int op){
col[u] = op;
for(auto v : G[u]){
if(col[v] == -1)dfs(v, op ^ 1);
}
}
void work(){
cin >> n >> m;
for(int i = 1, a, b; i < n; ++i){
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
mem(col, -1);
dfs(1, 0);
for(int i = 1, a, b; i <= m; ++i){
cin >> a >> b;
if(col[a] != col[b])cout << "Road\n";
else cout << "Town\n";
}
}
int main(){
io;
work();
return 0;
}
E – Shiritori
题目描述:
成语接龙,有n个长度大于等于
3
的字符串,当前说到s
,下次说的字符串的前3
个字符必须和s
的后三个完全相同
Takahashi
先手,Aoki
后手,二者轮流说成语,谁先不能说出成语则输,每个串都可以使用任意多次问以第
i
个字符串为开头的时候,谁会赢,如果陷入循环,则输出Draw
思路:
又是一个对抗博弈的题目
建图肯定不可取,我们发现,只存在大小写的字母,且只有三位有用,所以可以对每个串,哈希出前三位和后三位,得到 u
和v
,建u->v
就行考虑状态,以点
u
为起点时,如果先手必输,则ans[u]=0
,如果先手必赢,则ans[u]=1
- 如果一个点没有任何的出边,那这个点就是先手必输,即不存在串使的该点能转移到别的点
- 如果一个点一步只能走到先手必胜点,则他先手必输
- 如果一个点可以一步走到一个先手必输点,则他先手必赢
- 如果这个点既不是先手必胜点和不是先手必输点,则是平局点
对于一般来说的对抗博弈题,我们都是去跑
dfs
,但是这里存在可以循环的点,dfs
不好判,所以改成bfs
,改成bfs
的时候就需要注意了,之前跑dfs
的时候,我们是去递归跑完所有子节点,所以最先确定状态的其实所有出度为0
的点,如果是博弈树的话,也就是所有叶子节点,所以改成bfs
的时候,我们应该先确定叶子节点,也就是出度为0
的点,也就是需要我们反向建图,对于出度为0的点,他们的状态就是先手必输,状态为0
。遇到状态确定的点就跳过,否则,我们就给出度减1,如果当前点u
是先手必输,那对于u
的子节点v
,他的状态就是先手必胜,即1
,并给他塞队列里;如果v
的出度已经归0,此时他还没有确实状态,则说明能到达他的点都是必胜点,则他就是必输点,赋状态为0
,并给他塞到队列里输出结果的时候,我们应该判断后三个字母对应的状态,因为以当前串为起始串时,我们先手的人一定会用这个串,而我们求的状态可没有这个规定,所以我们判后三个字母为起始串就可以
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 300000 + 50
int n, m, k, x;
int tr[MAX];
string s;
int getid(char x){
if(x >= 'A' && x <= 'Z')return x - 'A';
else return x - 'a' + 26;
}
int calc(char a, char b, char c){
return getid(a) * 52 * 52 + getid(b) * 52 + getid(c);
}
vector<int>G[MAX];
int du[MAX];
int ans[MAX];
void work(){
cin >> n;
for(int i = 1; i <= n; ++i){
cin >> s;
int a = calc(s[0], s[1], s[2]);
int b = calc(s[(int)s.size() - 3], s[(int)s.size() - 2], s[(int)s.size() - 1]);
G[b].push_back(a);
++du[a];
tr[i] = b;
}
mem(ans, -1);
queue<int>q;
for(int i = 0; i < 52 * 52 * 52 + 5; ++i){
if(!du[i]){
ans[i] = 0;
q.push(i);
}
}
while (!q.empty()) {
int u = q.front();q.pop();
for(auto v : G[u]){
if(ans[v] != -1)continue;
--du[v];
if(ans[u] == 0){
ans[v] = 1;
q.push(v);
}
else if(du[v] == 0){
ans[v] = 0;
q.push(v);
}
}
}
for(int i = 1; i <= n; ++i){
int p = ans[tr[i]];
if(p == 0)cout << "Takahashi\n";
else if(p == 1)cout << "Aoki\n";
else cout << "Draw\n";
}
}
int main(){
io;
work();
return 0;
}