[考试反思]1106csp-s模拟测试103: 渺茫
7点之前上不了博客,用gedit写的。谅解一下。
看起来不是特别惨?但是被sdfz爆踩了。。。
而且其实并不能说“不是特别惨”吧
90分算个啥啊?还凑不出个T2的AC
难易度评估错误,T2是最简单的没看出来。然后爆搜不够优秀TLE 0.
T1伪证的60分只有20,如果外层加一个clock就有40.。。
T3部分分打满,很明显了却没有想到启发式合并
说实在的还是经验不足实力不够,面对难题原型毕露(虽说相对于简单题稍微好一些)
T1:Game
先不考虑字典序,如何得到最高的积分?
贪心。这个好说。
但是在后面的操作中还需要修改牌序,导致局面变化,还要统计动态的积分。
就是说,你需要依次考虑每一位应该填什么,然后删除这张牌再check一下看积分变不变。
在积分不变的基础上,这一位越大越好。这满足单调性可以二分(稍后具体讲)
其实这类似于单点修改操作(挺抽象的)。
暴力贪心的话那么就需要从头再重新做一遍。
考虑如何优化?那只能是数据结构了。
线段树分治。(基于值域)
对于A和B有的牌都开一个权值线段树。考虑合并两个子树(即线段树update)操作。
右儿子的A和左儿子的B可以结合(满足大小关系),这些是可以获胜的局面,全局累加答案。
即设$win=min(A_{rc},B_{lc})$。那么在上传的时候,就有$ans+=win,A_p=A_{lc}+A_{rc}-win,B_p=B_{lc}+B_{rc}-win$
就是已经胜利的局面累加积分并且不再参与以后的运算。最后全局ans的值就是最大积分。
这就是最优决策。已经是用尽量小的A去战胜B了,等价于贪心。
单点修改的话直接重置某一个下标的AB值,整条链都修改一下就好。
在修改的时候要注意全局的ans应该要删除原来这个节点的贡献再修改并update,否则积分就会重复计算。
现在说明一下它的单调性(证明二分):其实并不是直接二分,也不是完全的单调性。
先考虑如果你赢了这一位,那么你用的值越大,得分可能越低(浪费了)
如果你输了这一位,那么你用的值大了,总得分可能也会降低(也是浪费)
如果你可以在赢下这一位的基础上保证总积分不变,那么你就会赢下这一位(因为这样的话字典序会更大)
所以二分的过程其实是:检查这一位能不能赢,如果可以就在$[b_i+1,max]$二分,如果不能赢就在$[1,b_i]$二分
找到最大取值,在线段树里删除,同时也要把B删除。逐位考虑即可。
可以拿multiset维护一下剩余A的最大值作为二分上届。不然有可能被卡常(只有我被卡了。。。)
复杂度$O(nlog^2n)$
%%%Rock_B教我看懂标程
%%%yxs暴力踩暴正解(比正解还难写还需要一大堆特殊性质,咱也不会打%%%就是了)
据说建树容易被卡常,传参好像比建树全局调用快一些。
#include<cstdio>
#include<set>
using namespace std;
multiset<int>S;
int n,a[],b[],cl[],cr[],A[],B[];
int cnta[],cntb[],ans,rans;
void up(int p){
int nw=min(B[p<<],A[p<<|]);
ans+=nw;A[p]=A[p<<]+A[p<<|]-nw;B[p]=B[p<<]+B[p<<|]-nw;
}
void build(int p,int l,int r){
cl[p]=l;cr[p]=r;
if(l==r){A[p]=cnta[l];B[p]=cntb[l];return;}
build(p<<,l,l+r>>);build(p<<|,(l+r>>)+,r);
up(p);
}
void modify(int p,int pos){
if(cl[p]==cr[p]){A[p]=cnta[pos];B[p]=cntb[pos];return;}
ans-=min(B[p<<],A[p<<|]);
if(pos<=cr[p<<])modify(p<<,pos);
else modify(p<<|,pos);
up(p);
}
bool chk(int x,int p,int de){
cnta[x]--;modify(,x);
int rA=ans+de;
cnta[x]++;modify(,x);
return rA==rans;
}
int main(){
freopen("game.in","r",stdin);freopen("game.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;++i)scanf("%d",&b[i]),cntb[b[i]]++;
for(int i=;i<=n;++i)scanf("%d",&a[i]),cnta[a[i]]++,S.insert(a[i]);
build(,,*(--S.end()));rans=ans;
for(int i=;i<=n;++i){
cntb[b[i]]--;modify(,b[i]);
int l=b[i]+,r=*(--S.end()),ta=;
while(l<=r)if(chk(l+r>>,i,))ta=l+r>>,l=(l+r>>)+;else r=(l+r>>)-;
if(!ta){
int L=,R=b[i];
while(L<=R)if(chk(L+R>>,i,))ta=L+R>>,L=(L+R>>)+;else R=(L+R>>)-;
}else rans--;
cnta[ta]--,modify(,ta);printf("%d ",ta);S.erase(S.find(ta));
}puts("");
}
T2:Time
贪心。不能直接做就要去发现特殊元素或特殊性质。
考场上一直以为最大值是特殊元素然后就卡死了。(因为会对其他元素产生影响)
实际上是要考虑最小值。它最后一定在两侧,考虑它是往左还是右移就好了。
如果一个数出现了多次,那么就依次考虑是最左边的数往左移还是最右边的数往右移就好了。
#include<cstdio>
#include<vector>
using namespace std;
vector<int>v[];
int n,x[],t[];long long ans;
void add(int p,int w){for(;p<=n;p+=p&-p)t[p]+=w;}
int ask(int p,int a=){for(;p;p^=p&-p)a+=t[p];return a;}
main(){
freopen("time.in","r",stdin);freopen("time.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;++i)scanf("%d",&x[i]),v[x[i]].push_back(i),add(i,);
for(int i=;i<=;++i){
int h=,t=v[i].size()-;
while(h<=t){
int l=ask(v[i][h]-),r=ask(n)-ask(v[i][t]);
if(l<r)ans+=l,add(v[i][h],-),h++;
else ans+=r,add(v[i][t],-),t--;
}
}printf("%lld\n",ans);
}
T3:Cover
首先看到“包含或不相交”就知道要建树,有父子关系。建一个超级父节点控制[1,n]避免它是森林。
因为我脑子很清奇所以我的建树比较诡异,所有区间(包括超级根节点)按照l为第一关键字从小到大,r为第二关键字从大到小排序。
这样排序之后,你就得到了这棵树的dfs序,然后根据“dfs序下一个点的子树是一段连续的区间”,用全局的单调指针递归建树即可(详见代码,这个讲不好)
然后尝试dp。
因为我脑子比较清奇所以我并没有像题解一样设出那个dp然后再差分。
我的dp定义是f[i][j]表示对于i这个点,你给它「第」j次覆盖的机会时所得到的「额外」收益。
所以我的dp一设出来就是题解里那个数组的差分。
考虑转移,我给这个点一次覆盖机会,那么它的决策要么是把机会下传给所有儿子,要么是就用来覆盖自己这个区间
那么$f[i][j]=\sum f[son][j]$。在这样得到f数组之后它是单调的(你会采取最优决策来让它收益最大化,最优决策一定会在第一次被取出,以此类推)
所以f[p][1]>=f[p][2]>=f[p][3]...然而这样并没有考虑这个点本身。
所以在这个本身已经有序的数组里插入$w_p$就好了。然后超级根节点的f数组前缀和一下就是答案。
那25的部分分就不需要维护有序数组,而是直接开桶,把“倒深度”放进去再做前缀和就好了。
因为除了叶节点以外,一个点的子树大小一定大于自身大小,所以有覆盖机会就下传就好了。
给出暴力代码。
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
vector<long long>v[];
struct P{
int l,r,w,o;
friend bool operator<(P a,P b){
return a.l<b.l||(a.l==b.l&&a.r>b.r)||(a.l==b.l&&a.r==b.r&&a.o>b.o);
}
}ps[];
long long ans,Ans[];int n,m,sz[],l[],to[],cnt,fir[],ald=,spj=;
void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
void dfs(int p){
for(int i=fir[p];i;i=l[i])dfs(to[i]),sz[p]=max(sz[p],sz[to[i]]);
sz[p]++;v[p].resize(sz[p]);
for(int i=fir[p];i;i=l[i])for(int j=;j<sz[to[i]];++j)v[p][j]+=v[to[i]][j];
v[p][sz[p]-]=ps[p].w;
sort(v[p].begin(),v[p].end());reverse(v[p].begin(),v[p].end());
}
void build(int p){while(ps[p].l<=ps[ald].l&&ps[ald].r<=ps[p].r)link(p,ald),ald++,build(ald-);}
int DFS(int p){
int dep=;
for(int i=fir[p];i;i=l[i])dep=max(dep,DFS(to[i])+);
Ans[dep]+=ps[p].w;
return dep;
}
int main(){freopen("cover.in","r",stdin);freopen("cover.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i)scanf("%d%d%d",&ps[i].l,&ps[i].r,&ps[i].w),ps[i].o=i,ps[i].r--;
m++;ps[m]=(P){,n,,m};
for(int i=;i<m;++i)if(ps[i].w!=ps[].w)spj=;
sort(ps+,ps++m);build();
if(spj){DFS();for(int i=;i<m;++i)Ans[i]+=Ans[i-],printf("%lld ",Ans[i]);return ;}
dfs();
v[].resize(m);
for(int i=;i<m;++i)ans+=v[][i-],printf("%lld ",ans);puts("");
}
TLE70
如果用数组维护,时空都无法接受,考虑用数据结构。
需要支持的操作:插入一个值,不断取出最大值。(然后删除,这样次大值就变成了最大值,就可以依次取出并加和了)
大根堆。优先队列。
然而就这样空间对了时间复杂度还是有问题。
启发式合并,把重儿子的队列直接接过来,然后再与其它儿子合并。
复杂度$O(nlog^2n)$,并不知道是不是正解复杂度(为什么出了300000?)
update:我又害人了我写挂了,复杂度写成$O(n^2logn)$了。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<long long>v[],r;
struct P{
int l,r,w;
friend bool operator<(P a,P b){
return a.l<b.l||(a.l==b.l&&a.r>b.r);
}
}ps[];
long long ans,Ans[];int n,m,sz[],l[],to[],cnt,fir[],ald=,spj=;
void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
void dfs(int p){
for(int i=fir[p];i;i=l[i])dfs(to[i]),sz[p]=max(sz[p],sz[to[i]]);
sz[p]++;
for(int i=fir[p];i;i=l[i])if(sz[to[i]]+==sz[p]){swap(v[p],v[to[i]]);to[i]=;break;}
for(int i=fir[p];i;i=l[i])if(to[i]){
while(!v[to[i]].empty())r.push(v[p].top()+v[to[i]].top()),v[p].pop(),v[to[i]].pop();
swap(r,v[p]);while(!r.empty())v[p].push(r.top()),r.pop();
}v[p].push(ps[p].w);
}
void build(int p){while(ps[p].l<=ps[ald].l&&ps[ald].r<=ps[p].r)link(p,ald),ald++,build(ald-);}
int main(){freopen("cover.in","r",stdin);freopen("cover.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i)scanf("%d%d%d",&ps[i].l,&ps[i].r,&ps[i].w),ps[i].r--;
m++;ps[m]=(P){,n,};
sort(ps+,ps++m);build();
dfs();
for(int i=;i<=m;++i)v[].push();
for(int i=;i<m;++i)ans+=v[].top(),v[].pop(),printf("%lld ",ans);
}
错的
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<long long>v[],r;
struct P{
int l,r,w;
friend bool operator<(P a,P b){
return a.l<b.l||(a.l==b.l&&a.r>b.r);
}
}ps[];
long long ans,Ans[];int n,m,sz[],l[],to[],cnt,fir[],ald=,spj=;
void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
void dfs(int p){
for(int i=fir[p];i;i=l[i])dfs(to[i]),sz[p]=max(sz[p],sz[to[i]]);
sz[p]++;
for(int i=fir[p];i;i=l[i])if(sz[to[i]]+==sz[p]){swap(v[p],v[to[i]]);to[i]=;break;}
for(int i=fir[p];i;i=l[i])if(to[i]){
while(!v[to[i]].empty())r.push(v[p].top()+v[to[i]].top()),v[p].pop(),v[to[i]].pop();
while(!r.empty())v[p].push(r.top()),r.pop();
}v[p].push(ps[p].w);
}
void build(int p){while(ps[p].l<=ps[ald].l&&ps[ald].r<=ps[p].r)link(p,ald),ald++,build(ald-);}
int main(){freopen("cover.in","r",stdin);freopen("cover.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i)scanf("%d%d%d",&ps[i].l,&ps[i].r,&ps[i].w),ps[i].r--;
m++;ps[m]=(P){,n,};
sort(ps+,ps++m);build();
dfs();
for(int i=;i<=m;++i)v[].push();
for(int i=;i<m;++i)ans+=v[].top(),v[].pop(),printf("%lld ",ans);
}
对的
[考试反思]1106csp-s模拟测试103: 渺茫的更多相关文章
- [考试反思]0718 NOIP模拟测试5
最后一个是我...rank#11 rank#1和rank#2被外校大佬包揽了. 啊...考的太烂说话底气不足... 我考场上在干些什么啊!!! 20分钟“切”掉T2,又27分钟“切”掉T1 切什么切, ...
- [考试反思]0814NOIP模拟测试21
前两名是外校的240.220.kx和skyh拿到了190的[暴力打满]的好成绩. 我第5是170分,然而160分就是第19了. 在前一晚上刚刚爆炸完毕后,心态格外平稳. 想想前一天晚上的挣扎: 啊啊啊 ...
- [考试反思]1109csp-s模拟测试106:撞词
(撞哈希了用了模拟测试28的词,所以这次就叫撞词吧) 蓝色的0... 蓝色的0... 都该联赛了还能CE呢... 考试结束前15分钟左右,期望得分300 然后对拍发现T2伪了写了一个能拿90分的垃圾随 ...
- [考试反思]0909csp-s模拟测试41:反典
说在前面:我是反面典型!!!不要学我!!! 说在前面:向rank1某脸学习,不管是什么题都在考试反思后面稍微写一下题解. 这次是真的真的运气好... 这次知识点上还可以,但是答题策略出了问题... 幸 ...
- [考试反思]0729NOIP模拟测试10
安度因:哇哦. 安度因:谢谢你. 第三个rank1不知为什么就来了.迷之二连?也不知道哪里来的rp 连续两次考试数学都占了比较大的比重,所以我非常幸运的得以发挥我的优势(也许是优势吧,反正数学里基本没 ...
- [考试反思]0714/0716,NOIP模拟测试3/4
这几天时间比较紧啊(其实只是我效率有点低我在考虑要不要坐到后面去吹空调) 但是不管怎么说,考试反思还是要写的吧. 第三次考试反思没写总感觉缺了点什么,但是题都刷不完... 一进图论看他们刷题好快啊为什 ...
- [考试反思]1003csp-s模拟测试58:沉淀
稳住阵脚. 还可以. 至少想拿到的分都拿到了,最后一题的确因为不会按秩合并和线段树分治而想不出来. 对拍了,暴力都拍了.挺稳的. 但是其实也有波折,险些被卡内存. 如果内存使用不连续或申请的内存全部使 ...
- [考试反思]0816NOIP模拟测试23
210 210 210 170 还可以.暴力打满就rk4了? 但不管怎么说,总算是在改完题之后理直气壮的写考试反思了. T1是个dp,说水也不太水.(当然某脸只要A掉了一道题就要说那是水题) 我的思路 ...
- [考试反思]0801NOIP模拟测试11
8月开门红. 放假回来果然像是神志不清一样. 但还是要接受这个事实. 嗯,说好听点,并列rank#7. 说难听点,垃圾rank#18. 都不用粘人名就知道我是哪一个吧... 因为图片不能太长,所以就不 ...
随机推荐
- Eclipse导入别人项目爆红叉
1.导入项目之前,请确认工作空间编码已设置为utf-8:window->Preferences->General->Wrokspace->Text file encoding- ...
- JVM(三)初始化
字节码指令和符号引用.直接引用 1.主动引用和被动引用 主动引用:虚拟机规定只有满足四个情况的的情况下,才会进行主动引用. 被动引用:除过四种情况的引用是被动引用. 只有主动引用才会初始 ...
- Linux入门(服务)
LInux入门之 服务 服务介绍 常驻在内存中的程序,且可以提供一些系统或网络功能,那就是服务.比如: apache提供web服务 ftp提供文件下载上传服务 ssh提供了远程连接服务 防火墙提供了安 ...
- 分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离
分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论 ...
- 【JZOJ5248】花花的聚会
Description 注意测试数据中道路是 到 的单向道路,与题面恰好相反. Input Output Sample Input 7 7 1 3 1 2 6 7 3 6 3 5 3 4 7 2 3 ...
- RF用例执行方法
用例如下图: 1.执行整个项目下的所有用例 dos命令下输入robot D:\work_doc\RF (RF为下图中脚本项目Test目录的上级目录) 2.执行某个suite中的所有用例 dos命令下输 ...
- Spring整合Mybatis(IDEA版)
本文适用于初学者: 该文主要教大家如何整合spring和mybatis,整合完成效果,可以从数据库中查询出学生信息: 完整的工程目录如下: 整合思路: 需要spring来管理数据源信息. 需要spri ...
- 元素定位之css选择器(2)
理论学习地址:https://www.runoob.com/cssref/css-selectors.html 定位思路: 先在单元素范围内选择查找id或name,定位不到的话往上查扩大范围 使用实例 ...
- <学会提问-批判性思维指南>运用
引子 这是我第二遍读此书,我认为并且希望这次阅读对我整个人生产生深远的影响.人一出生身上带着母体的抵抗力,大概6个月以后开始渐渐消失,靠自身的抵抗力活着.30岁前很多人会带着上天给的运气,终有一天,用 ...
- 判断一字串String中是否包含某一串字符串
String ostype = data.getString("osType").toUpperCase(); //转换为大写 if (ostype.contains(" ...