"蔚来杯"2022牛客暑期多校训练营6补题 J , G , B , M
J Number Game 签到
推公式即可
代码:
#include <bits/stdc++.h>
#define endl '\n'
// #define int long long
using namespace std;
const int N = 205, M = 805;
int n, m;
int a[N], b[N];
int dp[N][21][M];
void solve()
{
int a,b,c,d;
cin>>a>>b>>c>>d;
int b1=a-b;
int b2=b;
int ans11=b1-c;
int ans12=b2-c;
int ans13=c;
int flag=0;
if(ans13==d||ans12==d||ans11==d) flag=1;
{
if(flag)
cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return;
}
int cnt=d-c;
int k1=a-2*b;
int ans1=cnt/k1;
if(ans1*k1==cnt&& ans1>=0) flag=1;
int k2=2*b-a;
int ans2=cnt/k2;
if(ans2*k2==cnt && ans2>=0) flag=1;
if(flag)
cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
G Icon Design 耐心题
n=5,直接打表
void solve(){
cin >> n;
for(int i = 1; i <= 4 * n + 5; i++){
for(int j = 1; j <= 13 * n + 19; j++){
if(i == 1 || i == 4 * n + 5){
cout <<"*";
}
else if(i <= 1 + n){
if(j == 1 || j == 13 * n + 19) cout <<"*";
else cout <<".";
}
else if(i <= 1 + n + 2 * n + 3){
if(j == 1 || j == 13 * n + 19) cout << "*";
else if(j <= 1 + n + 1) cout <<".";
else if(j <= 1 + n + 1 + 2 * n + 3){
if(j == 1 + n + 1 + 1 || j == 1 + n + 1 + 2 * n + 3){
cout <<"@";
}
else if(i == j - 1){
cout <<"@";
}
else cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1){
cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3){
if(j == 1 + 1 + n + 2 * n + 3 + n + 1 + 1) cout <<"@";
else if(i == 1 + n + 1) cout <<"@";
else if(i == 1 + n + n + 2) cout <<"@";
else cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1){
cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3){
if(j == 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 1){
cout <<"@";
}
else if(i == n + 1 + 2 * n + 3) cout <<"@";
else cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1){
cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3){
if(i == 1 + n + 1){
cout <<"@";
}
else if(i <= 1 + n + n + 1 && j == 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 1) cout <<"@";
else if(i == 1 + n + n + 1 + 1) cout <<"@";
else if(i >= n + 1 + n + 2 && i <= n + 1 + 2 * n + 3- 1 && j == 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3){
cout <<"@";
}
else if(i == 1 + n + 2 * n + 3){
cout <<"@";
}
else cout <<".";
}
else if(j <= 1 + 1 + n + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1){
cout <<".";
}
else cout <<"*";
}
else if(i <= 1 + n + 2 * n + 3 + n + 1){
if(j == 1 || j == 13 * n + 19){
cout <<"*";
}
else cout <<".";
}
//else if(i == 4 * n + 5)cout <<"*";
}
cout << endl;
}
}
B Eezie and Pie 树上差分 LCA
题意:
给出一棵的树,以1为根。每个点都有点权 d[i]。
对于点 i ,你需要在从 i点出现,向上跑 d[i] 个点。
求对于每个点,会被多少个点遍历到。
思路:
如果可以快速找出每个点的 \(k\) 级祖先,我们就可以用树上差分来求出最终的价值。
我们可以利用LCA中的树上倍增,在\(O(nlogn)\)的复杂度求出 \(k\) 级祖先。
当然也可以使用树链剖分
DFS代码:
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+10,M=2*N;
int n;
int cnt[N];
int to[M],pre[M], h[N],idx;
int fa[N][21],dep[N];
int d[N];
int ans[N];
void add(int a,int b){
to[idx]=b,pre[idx]=h[a],h[a]=idx++;
}
void dfs_lca(int u,int father){
dep[u]=dep[father]+1;
fa[u][0]=father;
for(int i=1;(1<<i)<=dep[u];i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=h[u];i!=-1;i=pre[i]){
int j=to[i];
if(j!=father){
dfs_lca(j,u);
}
}
}
int get_fa(int u,int k){
if(k==0) return u;
int temp=dep[u]-k;
for(int i=20;i>=0;i--)
if(dep[fa[u][i]]>temp)
u=fa[u][i];
return fa[u][0];
}
int dfs(int u,int father){
int res=d[u];
for(int i=h[u];i!=-1;i=pre[i]){
int j=to[i];
if(j!=father){
res+=dfs(j,u);
}
}
ans[u]=res;
return res;
}
void solve(){
memset(h,-1,sizeof h);
cin>>n;
for(int i=0;i<n-1;i++){
int u ,v;cin>>u>>v;
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++){
cin>>cnt[i];
}
dfs_lca(1,0);
for(int i=1;i<=n;i++){
d[i]++;
d[get_fa(i,max(0,cnt[i]+1))]--;
}
dfs(1,0);
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t--)
solve();
return 0;
}
BFS代码
BFS思路:将叶子节点放进去,从叶子节点BFS。
BFS有个注意点:要使用优先队列,保证严格的层次遍历。
const int N = 2e6 + 10;
int n, a[N];
int dep[N],fa[N][23];
vector<int> g[N];
int cnt[N], d[N], f[N];
bool vis[N];
void dfs(int x,int f){
dep[x]=dep[f]+1;
fa[x][0]=f;
for(int i=1;(1<<i)<=dep[x];i++){
fa[x][i]=fa[fa[x][i-1]][i-1];
}
for(int t: g[x]){
if(t!=f){
dfs(t,x);
}
}
}
int getfa(int x,int k){
if(k==0) return x;
int t=dep[x]-k;
for(int i=21;i>=0;i--){
if(dep[fa[x][i]]>t)
x=fa[x][i];
}
// if(fa[x][0]==0) return 1;
return fa[x][0];
}
void bfs(){
priority_queue<pii> q;//节点
for(int i = 2; i <= n; i++){
if(g[i].size() == 1){
q.push({dep[i],i});
}
}
while(!q.empty()){
pii t = q.top();
q.pop();
int x = t.sec;
int u = f[x];
cnt[u] += cnt[x];
if(!vis[u]){
q.push({dep[u], u});
vis[u] = 1;
}
}
}
void solve(){
cin >> n;
for(int i = 1; i <= n-1; i++){
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
for(int i = 1; i <= n; i++){
cin >> a[i];
}
dfs(1, 0);
for(int i = 1; i <= n; i++){
int f1 = getfa(i, 1);
int f2 = getfa(f1, a[i]);
f[i] = f1;
cnt[f2] --;
cnt[f1] ++;
}
bfs();
for(int i = 1; i <= n; i++){
if(i == 1) cout << cnt[i] + 1;
else cout <<" " << cnt[i] + 1;
}
}
官方题解:DFS栈
维护 dfs 栈,快速求出一个点的 𝑘 级祖先
在 dfs 时维护 dfs 栈,那么访问到点 𝑖 时,它的 𝑘 级祖先一定在当前栈内向下 𝑘 个。利用这个性质,可以 𝑂(𝑁) 求出所有的祖先。
代码:
void dfs_fa(int u,int father)
{
stk[++top]=u;
d[u]+=1;
d[stk[max(0,top-cnt[u]-1)]]-=1;
for(int i=h[u]; i!=-1; i=pre[i])
{
int j=to[i];
if(j!=father)
dfs_fa(j,u);
}
top--;
}
M Z-Game on grid 博弈论 DP
题意:
给定一个矩阵,上面有'A', 'B' 和 '.' 三种字符。
Alice和Bob在玩游戏,初始有一个棋子在 \((1,1)\)上,二人轮流操作棋子,每次可以选择向下和向右,不能出界。
- 遇到A,Alice胜,
- 遇到B,Bob胜
- 如果一直到了 \((n,m)\),则平局。
Alice先手。
问Alice是否有必胜、必平局、必败的策略。
思路:
我们枚举三次Alice想要的状态,设置为 k.
对于非 k 的状态,就是Alice不想要的。
并且,对于矩阵上的点(r,c),我们一定可以知道这个时候是谁在操作。
(因为 (1,1) 到 (r,c) 的操作数是固定的)
-
如果是Alice操作,两个方向可以获得的状态为 cnt1,cnt2。
只要这里有一个方向是Alice想要的,那么Alice就会走向它。 -
如果是Bob来操作,只要有一个方向不是k的,Bob就会选择这个方向。
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 510;
int n, m;
char a[N][N];
int k;
int dp[N][N];
bool check(int r, int c)
{ //检查是否越界
return 1 <= r && r <= n && 1 <= c && c <= m;
}
int dfs(int r, int c)
{
if (a[r][c] == 'A')
return 1;
if (a[r][c] == 'B')
return 2;
if (r == n && c == m)
return 3;
if (dp[r][c] != -1)
return dp[r][c];
// A
if ((r + c) % 2 == 0)
{
int cnt1 = 0, cnt2 = 0;
if (check(r + 1, c))
{
cnt1 = dfs(r + 1, c);
if (cnt1 == k)
return dp[r][c] = k;
}
if (check(r, c + 1))
{
cnt2 = dfs(r, c + 1);
if (cnt2 == k)
return dp[r][c] = k;
}
if (cnt1 == 0)
return dp[r][c] = cnt2;
else
return dp[r][c] = cnt1;
}
// B
else
{
int cnt1 = 0, cnt2 = 0;
if (check(r + 1, c))
{
cnt1 = dfs(r + 1, c);
if (cnt1 != k)
return dp[r][c] = cnt1;
}
if (check(r, c + 1))
{
cnt2 = dfs(r, c + 1);
if (cnt2 != k)
return dp[r][c] = cnt2;
}
if (cnt1 == 0)
return dp[r][c] = cnt2;
else
return dp[r][c] = cnt1;
}
}
void init()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
dp[i][j] = -1;
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i] + 1;
k = 1;
init();
if (dfs(1, 1) == k)
cout << "yes ";
else
cout << "no ";
k = 3;
init();
if (dfs(1, 1) == k)
cout << "yes ";
else
cout << "no ";
k = 2;
init();
if (dfs(1, 1) == k)
cout << "yes";
else
cout << "no";
cout << endl;
}
signed main()
{
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}