多校A层冲刺NOIP2024模拟赛26
挂完了,三道签只做出来一道,如此成绩如何NOIP?
随机游走
签1.
设\(ans_x\)表示以\(x\)为根的子树的答案,\(ts_x\)表示以\(x\)为根的子树的边权和,\(sum_x\)为以\(x\)为根的子树的点权和。
假设\(u\)的儿子的集合为\(\{v_1,v_2,\cdots\}\),设遍历顺序为\(v_1\sim v_{\cdots}\),假如交换\(x=v_i,y=v_{i+1}\)更优秀,那么有
即
所以按照这个东西排序就行,时间复杂度\(O(n\log n)\)。
不懂为什么题解写了那么长。
点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *Infile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
// FILE *Infile = stdin,*OutFile = stdout;
FILE *Infile = freopen("walk.in","r",stdin),*OutFile = freopen("walk.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
#define int long long
#define eb emplace_back
const int N = 5e5 + 10;
int n,a[N],sum[N],ans[N],ts[N];
struct Edge{int to,w;Edge(int T,int W):to(T),w(W){}};vector<Edge> e[N];
void dfs(int x){
sum[x] = a[x];
vector<pair<int,int> > res;
for(auto [y,w]:e[x]){
dfs(y);
ans[y] += w*sum[y];
ts[y] += w;
ans[x] += ans[y];
sum[x] += sum[y];
ts[x] += ts[y];
res.eb(ts[y],sum[y]);
}
sort(res.begin(),res.end(),[](pair<int,int> x,pair<int,int> y){
return 1.0*x.first/x.second< 1.0*y.first/y.second;});
ll tim = 0;
for(auto i:res){
ans[x] += i.second*tim;
tim += i.first;
}
}
inline void solve(){
cin>>n;
rep(i,2,n,1){int f,w;cin>>f>>w;e[f].eb(i,w);}
rep(i,1,n,1) cin>>a[i];
dfs(1);
cout<<ans[1]<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
solve();
}
分发奖励
签2,赛时唐氏没调出来,赛后改了改就过了。
发现如果一个节点的祖先有的,那么该节点也有,具有传递性,考虑dfs+线段树。
然后发现一次操作\((a,b)\)等价于将\(a\)和\(b\)的子树连上,将树拍成dfs序直接线段树区间加即可,最后查询为\(0\)的个数,答案即\(n-1-ct_0\)。
时间复杂度\(O(n\log n)\)。
点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *Infile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
// FILE *Infile = stdin,*OutFile = stdout;
FILE *Infile = freopen("reward.in","r",stdin),*OutFile = freopen("reward.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
#define eb emplace_back
namespace IO{
const int siz = 1<<20;
struct IO{
#define gc getchar_unlocked
#define push putchar_unlocked
template<class T>
inline void read(T &x){
x = 0;char s = gc();
for(;s < '0' || '9' < s;s = gc());
for(;'0' <= s && s <= '9';s = gc()) x = (x<<1) + (x<<3) + (s^48);
}
template<class T,class... Args>
inline void read(T &x,Args&... argc){read(x);read(argc...);}
template<class T>
inline void write(T x){
static int sta[20],top = 0;
do sta[++top] = x%10;while(x /= 10);
while(top) push(sta[top--]+'0');
}
inline void write(char x){push(x);}
template<class T,class... Args>
inline void write(T x,Args... argc){write(x);write(argc...);}
}io;
#define read io.read
#define write io.write
};using IO::io;
const int N = 5e5 + 10;
int n,q,ans[N];vector<int> e[N],have[N];
int in[N],out[N],tim;bool ok[N];
void dfs(int x){
in[x] = ++tim;
for(auto y:e[x]) dfs(y);
out[x] = tim;
}
struct Segment_Tree{
struct segment_tree{
int l,r,val,lz,ct;
#define l(x) tree[x].l
#define r(x) tree[x].r
#define val(x) tree[x].val
#define lz(x) tree[x].lz
#define ct(x) tree[x].ct
#define len(x) (r(x)-l(x)+1)
}tree[N<<2];
const int inf = 0x3f3f3f3f;
inline void pushup(int k){
int ls = k<<1,rs = k<<1|1;
val(k) = min(val(ls),val(rs));
if(val(k) == val(ls) && val(k) == val(rs)) ct(k) = ct(ls) + ct(rs);
else if(val(k) == val(ls)) ct(k) = ct(ls);
else ct(k) = ct(rs);
}
void build(int k,int l,int r){
l(k) = l,r(k) = r,lz(k) = 0;
if(l == r) return val(k) = 0,ct(k) = 1,void();
int mid = (l + r) >> 1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
pushup(k);
}
inline void mk(int k,int val){val(k) += val;lz(k) += val;}
inline void D(int k){if(lz(k)) mk(k<<1,lz(k)),mk(k<<1|1,lz(k)),lz(k) = 0;}
void upd(int k,int l,int r,int val){
if(l <= l(k) && r(k) <= r) return mk(k,val),void();
D(k);int mid = (l(k) + r(k)) >> 1;
if(l <= mid) upd(k<<1,l,r,val);
if(r > mid) upd(k<<1|1,l,r,val);
pushup(k);
}
inline int qry(){return val(1)==0?ct(1):0;}
}gt;
vector<int> sta;
void dfs(int x,bool pd){
if(ok[x]) gt.upd(1,in[x],out[x],1);
for(auto i:have[x]) gt.upd(1,in[i],out[i],1);
ans[x] = max(n-gt.qry()-1,0);
for(auto y:e[x]) dfs(y,pd);
if(ok[x]) gt.upd(1,in[x],out[x],-1);
for(auto i:have[x]) gt.upd(1,in[i],out[i],-1);
}
inline void solve(){
read(n,q);rep(i,2,n,1){int f;read(f);e[f].eb(i);}
dfs(1),gt.build(1,1,n);
rep(i,1,q,1){
int x,y;read(x,y);
have[x].eb(y),have[y].eb(x),ok[x] = ok[y] = true;
}
dfs(1,false);
rep(i,1,n,1) write(ans[i],' ');
}
signed main(){cin.tie(nullptr)->sync_with_stdio(false);solve();}
卡路里
签3.
设\(L_{i,j}\)表示左边第\(j\)种奶茶比第\(i\)个卡路里多的第一个店的位置\(+1\),\(R_{i,j}\)表示右边第\(j\)种奶茶比第\(i\)个卡路里多的第一个店的位置\(-1\),这里钦定\(a_{0,i}=a_{n+1,i}=inf\),这两个东西单调栈可以求。(这里的多为大于等于,不是严格大于)。
考虑一个点\((i,j)\)(即第\(i\)个店的第\(j\)种奶茶)何时有贡献,假如选的区间为\([l,r]\),如果\((i,j)\)有贡献当且仅当\(L_{i,j}\le l\le i\le r\le R_{i,j}\),将\(l,r\)看成两维,发现这就是一个矩形加,因为操作在查询前,用差分可以\(O(1)\)操作。最后枚举\([l,r]\)即可。
时间复杂度\(O(nm+m^2)\)。
点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
FILE *ErrFile = freopen("err.err","w",stderr);
#else
// FILE *InFile = stdin,*OutFile = stdout;
FILE *InFile = freopen("calorie.in","r",stdin),*OutFile = freopen("calorie.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 210,M = 5010;
int n,m,a[M][N],L[M][N],R[M][N];
ll sum[M][M],d[M];
inline void getl(int _){
stack<int> sta;a[0][_] = INT_MAX;
drep(i,m,0,1){
while(sta.size() && a[sta.top()][_] <= a[i][_]) L[sta.top()][_] = i+1,sta.pop();
sta.push(i);
}
}
inline void getr(int _){
stack<int> sta;a[m+1][_] = INT_MAX;
rep(i,1,m+1,1){
while(sta.size() && a[sta.top()][_] <= a[i][_]) R[sta.top()][_] = i-1,sta.pop();
sta.push(i);
}
}
inline void solve(){
cin>>n>>m;rep(i,2,m,1) cin>>d[i],d[i] += d[i - 1];
rep(i,1,m,1) rep(j,1,n,1) cin>>a[i][j];
rep(i,1,n,1) getl(i),getr(i);
rep(i,1,m,1) rep(j,1,n,1){
sum[L[i][j]][i] += a[i][j];sum[i+1][R[i][j]+1] += a[i][j];
sum[L[i][j]][R[i][j]+1] -= a[i][j];sum[i+1][i] -= a[i][j];
}
rep(i,1,m,1) rep(j,1,m,1) sum[i][j] += sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1];
ll ans = 0;
rep(l,1,m,1) rep(r,l,m,1){
ans = max(ans,sum[l][r]-d[r]+d[l]);
}
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
solve();
}
传话游戏
不会,滚去打板子了。
官方题解
p
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18570712