多校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}\)更优秀,那么有

\[ans_x+ts_y\times w_x + ans_y-ts_x\times w_y<ans_x+ans_y \]

\[ts_yw_x<ts_xw_y\Rightarrow\frac{ts_y}{w_y}<\frac{ts_x}{w_x} \]

所以按照这个东西排序就行,时间复杂度\(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();
}

传话游戏

不会,滚去打板子了。

官方题解

image

p

image

posted @ 2024-11-26 17:58  CuFeO4  阅读(39)  评论(0编辑  收藏  举报