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)
云落订出来这道题之后立刻写了题解呢,链接贴下面咯!
后记
嘻嘻——不嘻嘻——
完结撒花!

浙公网安备 33010602011771号