比赛总结
牛客周赛
周赛103
应该补的题目
E-新婚_牛客周赛 Round 103
题目大意
给定一颗树,每个节点有一个二进制的值,让你找到从一个祖先到他的孩子的点也可以相反 如果是相同就不行
如图1-3或者3-1是可一个,2-3就不行,lca为最近公共祖先
![[Pasted image 20250804153246.png]]
让你求出所有符合条件的u-v这样的二进制的值,同时给定询问看是否存在这样的值
题目主要实现思路
首先观察数据范围为2的20次方,一个节点代表一个二进制位树,所以最多遍历20个节点,相当于只用计算长度小于等于20的路径遍历整个树,并且记录每一个节点的父节点,回溯时,依次计算,每一个二进制值,
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int f[N];
int btoi(string s)
{
int res = 0;
for (int i = s.size() - 1; i >= 0; i--)
{
res = res * 2 + (s[i] - '0');
}
return res;
}
void solve()
{
int n, q;
cin >> n >> q;
string s;
cin >> s;
vector<vector<int>> adj(n);
for (int i = 0; i < n - 1; i++)
{
int a, b;
cin >> a >> b;
a--;
b--;
adj[a].push_back(b);
adj[b].push_back(a);
}
vector<int> vit(1 << 21, 0);
auto dfs = [&](auto &&self, int u, int fa) -> void
{
f[u] = fa;
for (int nu : adj[u])
{
if (nu != fa)
{
self(self, nu, u);
}
}
int nw = u, stp = 0;
string tps;
while (nw >= 0 && stp <= 20)
{
tps += s[nw];
string tp(tps);
reverse(tp.begin(), tp.end());
vit[btoi(tps.c_str())] = 1;
vit[btoi(tp.c_str())] = 1;
stp++;
nw = f[nw];
}
};
dfs(dfs, 0, -1);
while (q--)
{
int x;
cin >> x;
if (vit[x])
{
cout << "YES" << '\n';
}
else
{
cout << "NO" << '\n';
}
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
atcoder
abc417
abc418
应该补的题目
D - XNOR Operation
题目大意(自己总结)
给你一个优美字符串,给你一个操作,
If $S_i =$ 0 and $S_{i+1} =$ 0, let $x = 1$.
-If $S_i =$ 0 and $S_{i+1} =$ 1, let $x = 0$.
-If $S_i =$ 1 and $S_{i+1} =$ 0, let $x = 0$.
-If $S_i =$ 1 and $S_{i+1} =$ 1, let $x = 1$.
然后删除si,si+1,并且插入与x相对应的数字,求解真最后又多少个美丽字符串是T的子串
题目主要实现思路
首先子串问题优先考虑动态规划,观察题目样例,要么不改变0的个数,2,3步骤0的个数不会改变,要么一次减少2,所以说S包含偶数个0字符串,那就是一个优美字符串,子串问题 ,优先考虑动态规划,定义状态,前i个字符串,必须包含第i个字符中包含(0为偶数,1为奇数)个字符串的个数,根据状态转移方程依次累加求和即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int a[N];
void Jwe1()
{
int n;
cin >> n;
string s;
cin >> s;
s = " " + s;
vector<array<int, 2>> dp(n + 10); // 以a[i]字符结尾的i之前包含符合条件的子串有多少个
for (int i = 1; i < s.size(); i++)
{
if (s[i] == '0')
{
dp[i][0] = dp[i - 1][1];
dp[i][1] = dp[i - 1][0] + 1;//此时的0单独作为一个子串可以使奇数加一
}
else
{
dp[i][0] = dp[i - 1][0] + 1;//此时没有0可以使偶数加1
dp[i][1] = dp[i - 1][1];
}
}
int ans = 0;
for (int i = 1; i < s.size(); i++)
{
ans += dp[i][0];
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
T = 1;
// cin >> T;
while (T--)
Jwe1();
return 0;
}
E - Trapezium
题目大意(自己总结)
在一个二维平面上有 N 个点,其中第 i 个点的坐标为 (Xi,Yi) 。保证没有两个点在同一位置,也没有三个点是相交的。
在这些点的四点组合中,以这四点为顶点能组成梯形多边形的组合有多少种?
题目主要实现思路
梯形多边形有两条边平行,数据量较小,所以只需要遍历所有点,求出斜率,看是否斜率,相同,另外要特判一下矩形的个数会被算两次,减去矩形的个数除2;
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
const int mod = 998244353;
int a[N], b[N];
map<pair<double, int>, int> vit;
map<double, int> mp;
void solve()
{
int n;
cin >> n;
int ans = 0, p = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i] >> b[i];
for (int j = 1; j < i; j++)
{
double x;
if (b[i] == b[j])
{
x = 1e9;
}
else
{
x = (double(a[i] - a[j])) / ((double)b[i] - b[j]);
}
ans += mp[x];
mp[x]++;
int len = (a[i] - a[j]) * (a[i] - a[j]) + (b[i] - b[j]) * (b[i] - b[j]);
p += vit[{x, len}];
vit[{x, len}]++;
}
}
cout << ans - p / 2;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}
赛后总结
应该补的题目
D - Takahashi's Expectation
题目大意(自己总结)
有n份礼物,价值p,心情上升a,心情下降b,如果收到礼物价值大于p或者等于 心情加上a,如果小于p心情降低B,给定q个问题,初始心情为x求出收到所有礼物后的心情
- 1≤N≤10000
- 1≤Pi≤500 (1≤i≤N)
- 1≤Ai≤500 (1≤i≤N)
- 1≤Bi≤500 (1≤i≤N)
- 1≤Q≤5×105
- 0≤Xi≤109 (1≤i≤Q)
- All input values are integers.
题目主要实现思路
首先观察数据范围,特别小,而x的范围特别大,可以通过小范围的dp,定义状态dp【 i】[ j ] 等于收完第i个礼物之后的心情值,因为要求最终结果,可以着推,对于每一次询问的x,如果x过于大,最后肯定会一直减,x如果大于pre[ n ]
最后结果一定是x-所有n的和,如果x一直减到一个地方小于500,那就可以直接dp求出最后的结果
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
struct pa
{
int p, a, b;
};
pa g[N];
int pre[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> g[i].p >> g[i].a >> g[i].b;
pre[i] = pre[i - 1] + g[i].b;
}
vector<vector<int>> dp(n + 2, vector<int>(1010, 0)); // 第i个礼物之后的心情值
for (int i = 0; i < 1010; i++)
{
dp[n + 1][i] = i;
}
for (int i = n; i > 0; i--)
{
for (int j = 0; j < 1010; j++)
{
if (j > g[i].p)
{
dp[i][j] = dp[i + 1][max(0LL, j - g[i].b)]; //当前状态经过这一操作后到达下一状态
}
else
{
dp[i][j] = dp[i + 1][j + g[i].a];
}
}
}
int q;
cin >> q;
while (q--)
{
int x;
cin >> x;
int pos = lower_bound(pre + 1, pre + 1 + n, x - 500) - pre;//判断什么时候减到小于500
if (pos == n + 1)
{
cout << x - pre[n] << '\n';
}
else
{
cout << dp[pos][x - pre[pos-1]] << '\n';
}
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
E - A Path in A Dictionary
题目大意(自己总结)
给你一个图,让你找到从一个点到另一个点的路径边最小,最小定义为整数序列的词法顺序 如果下列 1. 或 2. 中的任一个成立,则整数序列 S=(S1,S2,…,S∣S∣) 在词法上小于整数序列 T=(T1,T2,…,T∣T∣) 。这里, ∣S∣ 和 ∣T∣ 分别表示 S 和 T 的长度。
∣S∣<∣T∣ 和 (S1,S2,…,S∣S∣)=(T1,T2,…,T∣S∣) 。
存在一些 1≤i≤min(∣S∣,∣T∣) 使得 (S1,S2,…,Si−1)=(T1,T2,…,Ti−1) 和 Si<Ti .
因为x-y路径起点终点一定相等,所以第一种不可能出现,所以尽可能选择最小的点,对与每一个边排序即可,有可能所有选最小的都到不了,所以按边排序dfs即可
题目主要实现思路
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
void solve()
{
vector<int> ans;
int n, m, x, y;
cin >> n >> m >> x >> y;
x--;
y--;
vector<vector<int>> adj(n);
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
a--;
b--;
adj[a].push_back(b);
adj[b].push_back(a);
}
for(auto &it:adj)
{
sort(it.begin(),it.end());
}
vector<int> vit(n);
auto dfs = [&](auto &&self, int u, int ed) -> void
{
vit[u] = 1;
if(ed==u)
{
for(auto it:ans)
{
cout<<it+1<<' ';
}
cout<<'\n';
return ;
}
for (auto nu : adj[u])
{
if (!vit[nu])
{
ans.push_back(nu);
self(self,nu,ed);
ans.pop_back();
}
}
};
ans.push_back(x);
dfs(dfs,x,y);
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
赛后总结
codeforces
Dashboard - Codeforces Round 1034 (Div. 3) - Codeforces
应该补的题目
E. MEX Count
题目大意(自己总结)
给你一个大小为n的非负整数数组A,求移除k个之后mex(A)的可能个数
题目主要实现思路
假设当前mex为x,假设当前mex=x,说明所有的x是必须删除的,0-x-1必须留一个,所以最少删除mp[ x ] x的个数,最多删除,n-x,所以只要能删除的个数在这个区间内,那么这个区间就可以使答案数多1,这样问题转化成了多次连续区间相加的问题,想到用差分解决,左端点为mp[ x ] 右端点为n-x,最后枚举mex即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
void solve()
{
int n;
cin >> n;
vector<int> a(n);
map<int, int> mp;
int mex=0;
for (int i = 0; i < n; i++)
{
cin >> a[i];
mp[a[i]]++;
}
for(int i=0;i<=n;i++)
{
if(mp[i]==0)
{
mex=i;
break;
}
}
vector<int>diff(n+10);
for(int i=mex;i>=0;i--)
{
diff[mp[i]]++;
diff[n-i+1]--;
}
for(int i=0;i<=n;i++)
{
if(i>=1)
{
diff[i]+=diff[i-1];
}
cout<<diff[i]<<' ';
}
cout<<'\n';
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
赛后总结
Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2)
应该补的题目
D. Root was Built by Love, Broken by Destiny
D. Root was Built by Love, Broken by Destiny
题目大意(自己总结)
给你一个图,把他分成二分图,对于每一条边,他所连接的两个点都位于河的两岸,在房屋之间画直线,桥梁不交叉
题目主要实现思路
这个图一定是树结构,如果为环那么就一定有多个点在同一边
![[Pasted image 20250809074652.png]]
如图所示,因此一定不为环,所以可以推断出来为树的结构,由简入繁,首先判断都在一边的
![[Pasted image 20250809075420.png]]
上图可以发现,如果第二层次的节点如果非叶子节点大于2的话,就一定会有交叉,
![[Pasted image 20250809075931.png]]
如果大于2层 那么非叶子节点只能为1
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
const int mod=1e9+7;
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n);
for (int i = 0; i < m; i++)
{
int x, y;
cin >> x >> y;
x--;
y--;
adj[x].push_back(y);
adj[y].push_back(x);
}
int ans = 2; // 第一个节点可以放在左边或者右边
if (m != n - 1)
{
cout << 0 << '\n';
return;
}
auto dfs = [&](auto &&dfs, int u, int fa, int s) -> int
{
int ch_num = 0, no_leaf = 0; // 孩子数目,非叶节点数目
for (auto nu : adj[u])
{
if (nu == fa)
continue;
no_leaf += dfs(dfs, nu, u, 1); // 以nu为根的节点数
ch_num++;
}
if (ch_num == 0)
{
return 0;
}
if (no_leaf > s) // 如果非叶子节点大于2会有交叉的情况无论怎么放ans=0
{
ans = 0;
return 1;
}
if (ch_num <= s) // 要么为1要么为2无论结果如何,其实都是一样的
{
ans = (ch_num*ans)%mod;
}
else
{
for (int i = 1; i <= ch_num - no_leaf; i++) // 叶子节点的阶乘
{
ans = (ans*i)%mod;
}
// 在乘以叶子节点的两边互相交换的次数
if (no_leaf)
ans =(s*ans)%mod;
}
return 1;
};
int idx = 0;
for (int i = 0; i < n; i++)
{
if (adj[i].size() > 1)
{
idx = i;
}
}
dfs(dfs, idx, idx, 2);
cout << ans%mod << '\n';
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
solve();
return 0;
}
赛后总结
Codeforces Round 1042 (Div. 3)
应该补的题目
D. Arboris Contractio
题目大意(自己总结)
给你一个无根树,你可以选择两个点,删除该路径上的所有边。并且连接到最初的那个点,求通过这个方法使直径最小化的方法
题目主要实现思路
这道题考察的也是贪心思想,通过样例我们就也能知道,最终的图形应该是有一个中心点连接着其他的点,这样这棵树的最短直径就是2(n == 2的时候除外,最小直径就为1,无需操作),所以我们就可以对所有的叶子节点动手,因为对叶子节点作为端点的话一次性可以使得更多的点与中心点相连,然后我们就需要找到哪一个点适合作为中心点,很明显,我们是要让所有的叶子节点对这个中心点进行一次操作,那么我们就需要找到连接最多的叶子节点的那一个节点,然后让叶子节点的数量减去这个节点所连接的叶子节点的数量即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
void solve()
{
int n;
cin >> n;
vector<vector<int>> adj(n);
for (int i = 0; i < n-1; i++)
{
int u, v;
cin >> u >> v;
u--;
v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
if (n == 2)
{
cout << 0 << '\n';
return;
}
int res=0;
for(int i=0;i<n;i++)
{
if(adj[i].size()==1)res++;
}
int mx=INT_MIN;
for(int i=0;i<n;i++)
{
int cnt=0;
for(auto &it:adj[i])
{
if(adj[it].size()==1)
{
cnt++;
}
}
mx=max(mx,cnt);
}
cout<<res-mx<<'\n';
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
T = 1;
cin >> T;
while (T--)
solve();
return 0;
}

浙公网安备 33010602011771号