2023牛客第六场补题报告A B C E G
A-Tree_2023牛客暑期多校训练营6 (nowcoder.com)
思路
可以考虑先进行重构树然后再进行树上dp进行状态转移。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e3+10;
const int inf = 1e18;
int a[N],c[N],fa[N],siz[N];
int dp[N][N];
int find(int x){
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void solve(){
int n; cin>>n;
for(int i = 1;i<=n;i++) cin>>a[i];
for(int i = 1;i<=n;i++) cin>>c[i];
vector<vector<int>>edge;
for(int i = 1;i<n;i++){
int u,v,w; cin>>u>>v>>w;
edge.push_back({w,u,v});
}
sort(edge.begin(),edge.end());
for(int i = 1;i<=n;i++){
siz[i] = 1; fa[i] = i;
dp[i][a[i]] = 0;
dp[i][a[i]^1] = -c[i];
}
for(auto e:edge){
int u = e[1],v = e[2],w = e[0];
u = find(u),v = find(v);
vector<int>tmp(siz[u]+siz[v]+1,-inf);
for(int j = 0;j<=siz[u];j++){
for(int k = 0;k<=siz[v];k++){
tmp[k+j] = max(tmp[j+k],dp[u][j]+dp[v][k]+1ll*j*(siz[v]-k)*w+1ll*(siz[u]-j)*k*w);
}
}
fa[v]=u;
siz[u]+=siz[v];
for(int j = 0;j<=siz[u];j++)dp[u][j]=tmp[j];
}
int ans = 0;
int root = find(1);
for(int i = 0;i<=n;i++)ans = max(ans,dp[root][i]);
cout<<ans<<'\n';
}
signed main(){
solve();
return 0;
}
B-Distance_2023牛客暑期多校训练营6 (nowcoder.com)
思路
很好想到是暴力枚举两端直接计算每个数对对答案的贡献次数,但是需要注意一个点就是如果直接暴力跑是一个 n 3 n^3 n3的复杂度,但是我们有公式,只要预处理出来所有的组合数就可以将内层的一个n优化为O(1)。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 998244353;
typedef pair<int, int> PII;
int n;
int qpow(int a, int b) {
int ans = 1, base = a;
while (b) {
if (b & 1) ans = ans * base % mod;
base = base * base % mod;
b >>= 1;
}
return ans % mod;
}
int k;
int fact[N];
int infact[N];
void init() {
fact[0] = 1;
infact[0] = 1;
for (int i = 1; i < N; i++) {
fact[i] = (fact[i - 1] * i) % mod;
}
infact[N - 1] = qpow(fact[N - 1], mod - 2);
for (int i = N - 2; i; i--) {
infact[i] = infact[i + 1] * (i + 1) % mod;
}
}
int C(int a, int b) {
if (a < b) return 0;
return (fact[a] * (infact[b] * infact[a - b] % mod)) % mod;
} //预处理阶乘,逆元求组合数
void solve() {
cin >> n;
vector<int> v1(n + 1), v2(n + 1);
for (int i = 1; i <= n; i++) {
cin >> v1[i];
}
for (int i = 1; i <= n; i++) {
cin >> v2[i];
}
sort(v1.begin() + 1, v1.end());
sort(v2.begin() + 1, v2.end());
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int l = C(i + j - 2, min(i - 1, j - 1));
// for (int k = 0; k <= min(i - 1, j - 1); k++) {
// l += C(i - 1, k) * C(j - 1, k);
// l %= mod;
// }
int r = C(n + n - i - j, min(n - i, n - j));
// for (int k = 0; k <= min(n - i, n - j); k++) {
// r += C(n - i, k) * C(n - j, k);
// r %= mod;
// }
res += (l * r % mod) * abs(v1[i] - v2[j]) % mod;
res %= mod;
}
}
cout << res << "\n";
}
signed main() {
IOS;
int t = 1;
// cin >> t;
init();
for (int i = 1; i <= t; i++) {
solve();
}
}
C-idol!!_2023牛客暑期多校训练营6 (nowcoder.com)
思路
我们打一个表就可以发现,其实每个含有5的倍数其实都是一个等差数列,所以直接枚举5的整数次幂然后使用求和公式相加即可,注意需要使用__int128答案可能会爆long long。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve();
signed main() {
// cin.sync_with_stdio(0);
// cin.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
void print(__int128 x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9)
print(x / 10); //注意这里是x>9不是x>10 (2019.10 wa哭了回来标记一下)
putchar(x % 10 + '0');
// putchar('\n');
}
__int128 df(int n) {
__int128 res = 0;
__int128 mul = 5;
while (mul <= n) {
//枚举mul
__int128 h = n / 2 - mul / 2;
__int128 cnt = (n / mul - 1) / 2 + 1;
res += (h + h - mul * (cnt - 1)) * cnt / 2;
// print(h);
// print(cnt);
// print(mul);
// putchar('\n');
// //枚举mul * 2
mul *= 2;
h = n / 2 - (mul - 1) / 2;
cnt = (n / mul);
mul /= 2;
res += (h + h - mul * (cnt - 1)) * cnt / 2;
// print(h);
// print(cnt);
// print(mul);
// cout << "****\n";
mul *= 5;
}
return res;
}
__int128 dfo(int n) {
__int128 res = 0;
__int128 mul = 5;
while (mul <= n) {
//枚举mul
res += (n / mul - 1) / 2 + 1;
mul *= 5;
}
return res;
}
void solve() {
int n;
cin >> n;
__int128 ans = df(n - (n & 1));
if (n & 1) {
ans += dfo(n);
}
print(ans);
}
E-Sequence_2023牛客暑期多校训练营6 (nowcoder.com)
思路
其实只需要分类讨论奇偶的情况,判断在一段中最多有多少个和为偶数的连续段,只要是大于等于k的就一定可行。但是这题比较复杂的一点是比较绕,很容易判断错误。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
template <typename T>
struct Fenwick {
int n;
vector<T> a;
Fenwick(int n) : n(n), a(n + 10) {}
void add(int x, T v) {
for (int i = x; i <= n; i += i & -i) {
a[i] += v;
}
}
T sum(int x) {
T ans = 0;
for (int i = x; i; i -= i & -i) {
ans += a[i];
}
return ans;
}
T rangeSum(int l, int r) { return sum(r) - sum(l - 1); }
int kth(T k) {
int x = 0;
for (int i = 1 << std::__lg(n); i; i /= 2) {
if (x + i <= n && k >= a[x + i - 1]) {
x += i;
k -= a[x - 1];
}
}
return x;
}
};
int ji[N],ou[N];
void solve() {
int q;
cin >> n >> q;
Fenwick<int> bi(n);
fill(ji,ji+n+1,0);
fill(ou,ou+1+n,0);
vector<int> place_1;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
a[i] = x % 2;
if(a[i]) place_1.push_back(i);
bi.add(i, x % 2);
}
vector<int> ma(n + 1);
for(int i = 1,len = 0;i <= n;i ++) {
if(a[i] == 1) {
ma[i] = ++len;
}
}
for(int i = 1,j = 0,k = 0,start;i <= n;i ++){
ji[i] += ji[i-1];
if(a[i] == 1 && j == 0) {
k = 0;
j = 1;
start = i;
}
else if(a[i] == 1 && j == 1) {
k++;
for(int ll = start;ll <= i;ll ++) ji[ll] += k;
j = 0;
}
if(j == 1){
k++;
}
}
for(int i = 1,j = 0,k = 0,start,not_first = 0;i <= n;i ++){
ou[i] += ou[i-1];
if(a[i] == 1) {
if(not_first == 0) {
not_first = 1;
continue;
}
}
if(a[i] == 1 && j == 0) {
k = 0;
j = 1;
start = i;
}
else if(a[i] == 1 && j == 1) {
k++;
for(int ll = start;ll <= i;ll ++) ou[ll] += k;
j = 0;
}
if(j == 1){
k++;
}
}
//for(int i = 1;i <= n;i ++) cout << ou[i] << " ";cout << endl;
while (q--) {
int l, r, k;
cin >> l >> r >> k;
int tmp = bi.rangeSum(l, r);
if (tmp % 2 == 0 && r - l + 1 >= k) {
if(tmp == 0) {
cout << "YES" << endl;
continue;
}
int pla_1 = lower_bound(place_1.begin(),place_1.end(),l) - place_1.begin();
pla_1 = place_1[pla_1];
if(ma[pla_1] % 2) {
int sum = ji[r] - ji[l - 1];
int num = (r - l + 1) - sum + tmp / 2;
if(num >= k) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
} else {
int sum = ou[r] - ou[l - 1];
int num = (r - l + 1) - sum + tmp / 2;
if(num >= k) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
} else
cout << "NO\n";
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
G-Gcd_2023牛客暑期多校训练营6 (nowcoder.com)
思路
我们可以观察到,其实z一定要是x,y最大公约数的倍数。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
int x, y, z;
cin >> x >> y >> z;
x = abs(x);
y = abs(y);
z = abs(z);
if (x > y) swap(x, y);
if (x == 0) {
if (z != y && z != 0)
cout << "NO\n";
else
cout << "YES\n";
} else {
if (z % __gcd(x, y) == 0 && z != 0)
cout << "YES\n";
else
cout << "NO\n";
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}