1.13 下午-模拟赛

前言

勿让将来,辜负曾经

I cannot live without...

正文

知识点

打个表?

题目 个人感觉难度 知识点 预估分数&实际分数&耗时 感想
T1 NOIP T1- 线段树简单应用 \(100pts/100pts\) \(0.5h\) 卡的时间有点久了,线段树不熟练捏
T2 NOIP T2+ 区间 DP \(100pts/100pts\) \(1h\) 被这道题降智了四十来分钟,隔壁的 FJJ 一眼就秒了,就很难评
T3 NOIP T4+ 基环树+树形 DP+线段树合并 \(0pts/0pts\) \(2h\) 感慨自己的代码能力,明明已经口胡出 \(79pts\) 的做法,但是时间走完了最后还是没有调出来……

一题一解

T1 开关(P3870)

链接

维护两个操作,区间取反,区间求和,线段树是显然的

需要设计一下 pushdown 函数,注意到对于同一个区间,开关偶数次是没有任何实际意义的。所以维护一个 tag,表示是否取反

下传的时候更新 sum 以及儿子区间的 tag

Warning:不要忘记建树……

点击查看代码
#include<iostream>
#define endl '\n'
#define int long long
using namespace std;
const int maxn=1e5+10;
int n,m;
struct Segment_tree{
    struct node{
        int l,r,sum,tag;
    }tr[maxn<<2];
    void pushup(int u){
        tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
        return;
    }
    void pushdown(int u){
        if(tr[u].tag==0){
            return;
        }
        tr[u<<1].sum=tr[u<<1].r-tr[u<<1].l+1-tr[u<<1].sum;
        tr[u<<1|1].sum=tr[u<<1|1].r-tr[u<<1|1].l+1-tr[u<<1|1].sum;
        tr[u<<1].tag^=1;
        tr[u<<1|1].tag^=1;
        tr[u].tag=0;
        return;
    }
    void build(int u,int l,int r){
        tr[u].l=l;
        tr[u].r=r;
        if(l==r){
            return;
        }
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        pushup(u);
        return;
    }
    void modify(int u,int ql,int qr){
        int l=tr[u].l,r=tr[u].r;
        if(ql<=l&&qr>=r){
            tr[u].sum=(r-l+1)-tr[u].sum;
            tr[u].tag^=1;
            return;
        }
        pushdown(u);
        int mid=l+r>>1;
        if(ql<=mid){
            modify(u<<1,ql,qr);
        }
        if(qr>mid){
            modify(u<<1|1,ql,qr);
        }
        pushup(u);
        return;
    }
    int query(int u,int ql,int qr){
        int l=tr[u].l,r=tr[u].r;
        if(ql<=l&&qr>=r){
            return tr[u].sum;
        }
        pushdown(u);
        int mid=l+r>>1,res=0;
        if(ql<=mid){
            res+=query(u<<1,ql,qr);
        }
        if(qr>mid){
            res+=query(u<<1|1,ql,qr);
        }
        return res;
    }
}Tr;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    Tr.build(1,1,n);
    while(m--){
        int opt,x,y;
        cin>>opt>>x>>y;
        if(opt==0){
            Tr.modify(1,x,y);
        }else{
            int ans=Tr.query(1,x,y);
            cout<<ans<<endl;
        }
    }
    return 0;
}

T2 Cheapest Palindrome G(P2890)

链接

最值问题,贪心二分和 DP 排着队来呗,显然动态规划。进一步地注意到数据范围,考虑区间 DP

套路地,记 \(f_{l,r}\) 表示使区间 \([l,r]\) 变为回文串的最小花费,初始化为 \(+ \infty\)

考虑转移

易知 \(f_{l,r}\)\(f_{l+1,r},f_{l,r-1},f_{l+1,r-1}\) 转移过来,进行简单分类讨论

如果 \(f_{l,r}\)\(f_{l+1,r}\) 转移,在右端添加字符或删去左端的字符,价值取 \(\min\)\(f_{l,r-1}\) 同理;而对于 \(f_{l+1,r-1}\),当且仅当 s[l]==s[r] 的时候可以转移

警示后人:当 len==2 时,\(f_{l,r}=0\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e3+5;
int n,m;
char s[maxn];
map<char,int> mp;
int f[maxn][maxn];
inline void init(){
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=m;i++){
		f[i][i]=0;
	}
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>(s+1);
	for(int i=1;i<=n;i++){
		char opt;
		int x,y;
		cin>>opt>>x>>y;
		mp[opt]=min(x,y);
	}
	init();
	for(int len=2;len<=m;len++){
		for(int l=1;l+len-1<=m;l++){
			int r=l+len-1;
			int vl=mp[s[l]],vr=mp[s[r]];
			if(s[l]==s[r]){
				if(len==2){
					f[l][r]=0;
				}else{
					f[l][r]=min(f[l][r],f[l+1][r-1]);
				}
			}else{
				f[l][r]=min(f[l+1][r]+vl,f[l][r-1]+vr);
			}
		}
	}
	cout<<f[1][m]<<endl;
	return 0;
}

T3 最悪の記者 4(P7563)

链接

云落订出来这道题之后立刻写了题解呢,链接贴下面咯!

传送门

后记

嘻嘻——不嘻嘻——

完结撒花!

posted @ 2025-03-07 20:22  sunxuhetai  阅读(17)  评论(1)    收藏  举报