26.3.25 1600-1900 板刷日记
2192 D. Cost of Tree
tag: DFS,树形 DP
对以 \(u\) 为根的树来说,只有三种选择(下面的子树是指:以 \(u\) 的子节点 \(v\) 为根节点的子树):
- 不操作,这个答案是可以简单的预处理出来的
- 操作,且操作在某个子树内部:直接枚举是哪个子树被操作即可
- 操作,且操作在子树外部,这种情况下,一定是直接选择整个子树,接到其他子树的最深处。
对于第三种情况,维护每个子树的最深深度,然后枚举要移动的子树即可,期间需要记录所有子树的深度最大值和次大值
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<vector<int>> g(n+1);
vector<int> a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> f(n+1),dp(n+1),dep(n+1),mxdep(n+1),sz(n+1);
auto dfs1=[&](auto dfs,int u,int pre)-> void {
dep[u]=dep[pre]+1;
sz[u]=a[u];
mxdep[u]=dep[u];
for(auto v:g[u]){
if(v==pre) continue;
dfs(dfs,v,u);
sz[u]+=sz[v];
f[u]+=f[v]+sz[v];
mxdep[u]=max(mxdep[u],mxdep[v]);
}
};
dfs1(dfs1,1,0);
auto dfs2=[&](auto dfs2,int u,int pre)-> void {
vector<int> t;
dp[u]=f[u];
for(auto v:g[u]){
if(v==pre) continue;
dfs2(dfs2,v,u);
t.push_back(mxdep[v]);
}
sort(t.begin(),t.end(),greater<int>());
for(auto v:g[u]){
if(v==pre) continue;
dp[u]=max(dp[u],f[u]-f[v]+dp[v]);
}
if(t.size()<2) return;
for(auto v:g[u]){
if(v==pre) continue;
if(mxdep[v]!=t[0]){
dp[u]=max(dp[u],f[u]+sz[v]*(t[0]-dep[v]+1));
}
else{
dp[u]=max(dp[u],f[u]+sz[v]*(t[1]-dep[v]+1));
}
}
};
dfs2(dfs2,1,0);
for(int i=1;i<=n;i++){
cout<<dp[i]<<" ";
}
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
2190 B1. Sub-RBS (Easy Version)
tag: 括号序列,构造
因为原序列是一个合法序列,所以可以考虑直接将第一个 ) 删掉,再将后面只要删除最后一个 ( 即可
注意删除删除最后一个 ( 会更优,仅当第一个 ) 后有至少两个 ( 才会满足
Hard Version DP 不会做
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
string s;
cin>>s;
s=" "+s;
int pos=s.find(')');
int cnt=0;
for(int i=pos+1;i<=n;i++){
if(s[i]=='(') cnt++;
}
if(cnt<2) cout<<-1;
else cout<<n-2;
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
2189 D1. Little String (Easy Version)
tag: MEX,数学
对于排列上的 MEX,有一个定理:
对于一个 \(0\) 到 \(n-1\) 的排列,它的某个区间 \([l,r]\) 的 MEX 等于这个这个区间前后缀 (\([1,l),(r,n]\)) 的最小值
所以对于 \(w_i=1\),等价于 \(i\) 是排列的一个前缀或后缀最小值
所以对于 \(w_i=0\),等价于 \(i\) 不是排列的一个前缀或后缀最小值
特殊的,\(w_0=0\) 或 \(w_n=0\) 不存在
填数字的时候从大到小填,设此时还有 \(t\) 个数字没填,如果这个数字需要是一个最小值(\(2\) 个位置),则将其填到两边,否则填到中间 (\(t-2\) 个位置)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 1e9+7;
void solve(){
int n,c;
cin>>n>>c;
string s;
cin>>s;
s=" "+s;
vector<int> a(n+1);
for(int i=1;i<=n;i++){
a[i]=s[i]-'0';
}
if(a[1]==0 || a[n]==0){
cout<<-1<<endl;
return;
}
int ans1=1,ans2=1;
int now=n-2;
for(int i=n-1;i;i--){
if(s[i]=='0'){
ans1=ans1*now%mod;
ans2=ans2*now%c;
}
else{
ans1=ans1*2%mod;
ans2=ans2*2%c;
}
now--;
}
if(ans2==0) cout<<-1<<endl;
else cout<<ans1<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
2184 G. Nastiness of Segments
tag:二分+线段树,线段树二分
本题时间宽松, \(O(n*log^2n)\) 的二分+线段树也可通过,不必线段树上二分
对于 \(d\),是单调递增的,而 \(min\) 一定是递减的,所以一定是最多只一个交界点,二分交界点即可,线段树维护
最后再 \(check\) 一下,以防没有交界点
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
class SegmentTree {
public:
#define lc u<<1
#define rc u<<1|1
struct Node {
int l, r, sum;
int maxv, minv;
int add, cov;
bool has_cov; // 标记是否存在覆盖操作,因为覆盖值可能是 0
};
int n;
vector<int> w;
vector<Node> tr;
SegmentTree(int n) {
init(n);
}
void init(int n) {
this->n = n;
w.assign(n + 10, 0);
tr.resize(4 * n + 10);
}
void pushup(int u) {
tr[u].sum = tr[lc].sum + tr[rc].sum;
tr[u].maxv = max(tr[lc].maxv, tr[rc].maxv);
tr[u].minv = min(tr[lc].minv, tr[rc].minv);
}
// 辅助函数:处理覆盖逻辑
void apply_cov(int u, int k) {
tr[u].cov = k;
tr[u].has_cov = true;
tr[u].add = 0; // 覆盖后,之前的加法标记失效
tr[u].sum = (tr[u].r - tr[u].l + 1) * k;
tr[u].maxv = k;
tr[u].minv = k;
}
// 辅助函数:处理加法逻辑
void apply_add(int u, int k) {
tr[u].add += k;
tr[u].sum += (tr[u].r - tr[u].l + 1) * k;
tr[u].maxv += k;
tr[u].minv += k;
}
void pushdown(int u) {
if (tr[u].has_cov) {
apply_cov(lc, tr[u].cov);
apply_cov(rc, tr[u].cov);
tr[u].has_cov = false;
tr[u].cov = 0;
}
if (tr[u].add) {
apply_add(lc, tr[u].add);
apply_add(rc, tr[u].add);
tr[u].add = 0;
}
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0, 0, 0, 0, false};
if (l == r) {
tr[u].sum = tr[u].maxv = tr[u].minv = w[l];
} else {
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
}
// 区间加
void modify_add(int u, int l, int r, int k) {
if (l <= tr[u].l && r >= tr[u].r) {
apply_add(u, k);
} else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify_add(lc, l, r, k);
if (r > mid) modify_add(rc, l, r, k);
pushup(u);
}
}
// 区间覆盖
void modify_cov(int u, int l, int r, int k) {
if (l <= tr[u].l && r >= tr[u].r) {
apply_cov(u, k);
} else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify_cov(lc, l, r, k);
if (r > mid) modify_cov(rc, l, r, k);
pushup(u);
}
}
int query_sum(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum;
pushdown(u);
int res = 0, mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) res += query_sum(lc, l, r);
if (r > mid) res += query_sum(rc, l, r);
return res;
}
int query_max(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) return tr[u].maxv;
pushdown(u);
int res = -2e18, mid = tr[u].l + tr[u].r >> 1; // 假设使用 long long
if (l <= mid) res = max(res, query_max(lc, l, r));
if (r > mid) res = max(res, query_max(rc, l, r));
return res;
}
int query_min(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) return tr[u].minv;
pushdown(u);
int res = 2e18, mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) res = min(res, query_min(lc, l, r));
if (r > mid) res = min(res, query_min(rc, l, r));
return res;
}
};
void solve(){
int n,q;
cin>>n>>q;
SegmentTree tr(n);
for(int i=1;i<=n;i++){
cin>>tr.w[i];
}
tr.build(1,1,n);
while(q--){
int ch;
cin>>ch;
if(ch==1){
int i,x;
cin>>i>>x;
tr.modify_cov(1,i,i,x);
}
else{
int a,b;
cin>>a>>b;
int l=0,r=b-a;
while(l<r){
int mid=(l+r+1)/2;
if(tr.query_min(1,a,a+mid)<mid) r=mid-1;
else l=mid;
}
if(tr.query_min(1,a,a+l)==l) cout<<1<<endl;
else cout<<0<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
2185 E. The Robotic Rush
tag: 模拟,排序
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,m,k;
cin>>n>>m>>k;
vector<int> a(n+1),b(m+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i];
}
sort(b.begin()+1,b.end());
b[0]=-inf;
b.push_back(inf);
vector<pii> l(n+1),r(n+1);
for(int i=1;i<=n;i++){
int it=upper_bound(b.begin()+1,b.end(),a[i])-b.begin();
r[i]={b[it]-a[i],i};
it=lower_bound(b.begin()+1,b.end(),a[i])-b.begin()-1;
l[i]={a[i]-b[it],i};
}
sort(l.begin()+1,l.end(),greater<pii>());
sort(r.begin()+1,r.end(),greater<pii>());
int L=0,R=0;
int now=0;
int ans=n;
vector<int> st(n+1,1);
while(k--){
char ch;
cin>>ch;
if(ch=='L'){
now--;
}
else now++;
L=max(L,-now);
R=max(R,now);
while(l.size()>1 && L>=l.back().first){
auto [_,id]=l.back();
l.pop_back();
if(st[id]){
// if(ans==0) cout<<id<<" "<<st[id]<<endl;
ans--;
st[id]=0;
// if(ans==-1) cout<<id<<" "<<st[id]<<endl;
}
}
while(r.size()>1 && R>=r.back().first){
auto [_,id]=r.back();
r.pop_back();
if(st[id]){
ans--;
st[id]=0;
}
}
cout<<ans<<" ";
}
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
2185 F. Cherry Tree
tag:树形 DP
设 \(f[u][j:0/1/2]\) 表示,对于以 \(u\) 为根的子树,可以不可以用 \(j\) 次操作处理掉子树内的
转移一下子树的 \(DP\) 值即可
另一个思路是,统计叶节点个数,比如有 \(4\) 个,就尝试找一下有没有一个节点可以直接处理两个叶节点
或者有没有 \(2\) 个不相交且都可以处理 \(3\) 个叶节点的节点
但这个思路需要注意很多细节,很难写。
代码是 DP 写法
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<vector<int>> g(n+1);
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
int sum=0;
vector<int> w(n+1);
for(int i=2;i<=n;i++){
if(g[i].size()==1){
sum++;
w[i]=1;
}
}
sum%=3;
if(sum==0){
cout<<"YES\n";
return;
}
auto dfs1=[&](auto dfs1,int u,int pre)-> void {
for(auto v:g[u]){
if(v==pre) continue;
dfs1(dfs1,v,u);
w[u]+=w[v];
w[u]%=3;
}
};
dfs1(dfs1,1,0);
auto dfs2=[&](auto dfs2,int u,int pre)-> vector<int> {
vector<int> st(3);
if(u!=1 && g[u].size()==1){
st[1]=1;
return st;
}
st[0]=1;
for(auto v:g[u]){
if(v==pre) continue;
auto f=dfs2(dfs2,v,u);
vector<int> tmp(3);
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(f[i] && st[j]){
tmp[(i+j)%3]=1;
}
}
}
st=tmp;
}
st[1]=1;
return st;
};
auto st=dfs2(dfs2,1,0);
if(st[0]) cout<<"YES\n";
else cout<<"NO\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}

浙公网安备 33010602011771号