[CSP-S模拟测试50]反思+题解
??大部分人都觉得T3是道不可做题去刚T1T2了,于是我就侥幸苟到了前面?
这场考试比较成功的就是快速水掉了T1T2的部分分,1h拿到88分起码为之后硬肝T3上了保险(赛后发现就算T3爆零也能rank15?)
剩下也就没什么了……T3的分完全是时间堆出来的,还有运气成分。因为当时第一个A掉了二分答案专题的奶牛健美操那道题,所以看到直径下意识想维护子树最长链+次长链,而且要不是前面两道题都不会我才不敢写那个恶心至极的分类讨论换根QAQ。单就方法而言,我打的东西其实挺无脑的,并没有思考太多针对本题的性质(要不然我还能只拿前两问的分?),最后莫名其妙不会第三问了。剩下的时间也没有打中途想到的二维莫队,觉得时间复杂度没有保证得不偿失。最后几十分钟没怎么拿分。
不过拿到T3的分至少证明自己代码能力和调试能力有了提升?(YY什么呢还是一如既往的菜)
看到自己前面的4位大佬都A题了,差一点碾碎我的LockeyA掉了T2,自己不过是三道题都骗了点分而已。前路还漫长啊。
A.施工
神dp。除了单调栈还有二次函数优化??
至于为什么尽量把坑填到和某一栋楼一样平是最优的,我也不会证
把当前位置$i$作为坑的右边缘,枚举坑的左边缘$j$,找能填的位置$k$,可得转移:
$f[i]=\sum \limits _{k=j+1}^{i-1}(t-h_k)^{2}+c*(h_j+h_i-2*t)+f[j]$
想怎么优化转移。首先要避免枚举所有位置。可以维护一个单调递减的单调栈,这样就确保栈里的元素一定可以转移给当前的$i$。
然后进行寻找最优解的优化。观察到这个转移方程是一个二次函数,化简一下就可以得到它的一次项、二次项、常数项系数。
那自变量$t$当然是取对称轴处($- \frac{b}{2a}$)最优啦,但是要注意取对称轴的答案是否合法。特判之。
计算对称轴四舍五入。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n;
ll C,h[N],sum[N],Sum[N],f[N];
int s[N],top;
ll cacl(int l,int r,ll val)
{
ll a=r-l-1,b=-2*(sum[r-1]-sum[l]);
if(l)b-=C;if(r!=n+1)b-=C;
ll c=Sum[r-1]-Sum[l];
if(l)c+=h[l]*C;if(r!=n+1)c+=h[r]*C;
ll mid=-1LL*floor((double)b/((double)a*2.0)+0.5);
mid=max(mid,val);
if(l)mid=min(mid,h[l]);
if(r!=n+1)mid=min(mid,h[r]);
return mid*mid*a+mid*b+c;
}
int main()
{
scanf("%d%lld",&n,&C);
for(int i=1;i<=n;i++)
{
scanf("%lld",&h[i]);
sum[i]=sum[i-1]+h[i];
Sum[i]=Sum[i-1]+h[i]*h[i];
}
h[0]=h[n+1]=0x7fffffff;
for(int i=1;i<=n+1;i++)
{
if(i!=1&&i!=n+1)f[i]=f[i-1]+abs(h[i]-h[i-1])*C;
else f[i]=f[i-1];
while(top&&h[s[top]]<=h[i])f[i]=min(f[i],f[s[top-1]]+cacl(s[top-1],i,h[s[top]])),top--;
s[++top]=i;
}
cout<<f[n+1]<<endl;
return 0;
}
B.蔬菜
二维莫队水之。注意指针移动的先后顺序。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=205,M=3e5+5;
int n,m,Q;
int a[N][N],type,bl,ans[M],res,bu[N*N];
map<int,int> link;
struct query
{
int x,y,xx,yy,id;
friend bool operator < (query p,query q)
{
return p.x/bl==q.x/bl?(p.y/bl==q.y/bl?(p.xx/bl==q.xx/bl?p.yy<q.yy:p.xx<q.xx):p.y<q.y):p.x<q.x;
}
}q[M];
void add(int x)
{
//cout<<"A "<<bu[x]<<endl;
res+=2*bu[x]+1;
bu[x]++;
}
void del(int x)
{
//cout<<"D "<<bu[x]<<endl;
res-=(2*bu[x]-1);
bu[x]--;
}
int main()
{
n=read();m=read();Q=read();
bl=pow(n*m,0.5)/pow(Q,0.25)+1.0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int val=read();
if(!link[val])link[val]=++type;
a[i][j]=link[val];
}
for(int i=1;i<=Q;i++)
q[i].x=read(),q[i].y=read(),q[i].xx=read(),q[i].yy=read(),q[i].id=i;
sort(q+1,q+Q+1);
int x=1,y=1,xx=0,yy=0;
for(int i=1;i<=Q;i++)
{
int qx=q[i].x,qy=q[i].y,qxx=q[i].xx,qyy=q[i].yy;
//cout<<"POS "<<x<<' '<<y<<' '<<xx<<' '<<yy<<' '<<endl;
while(x>qx){x--;for(int j=y;j<=yy;j++)add(a[x][j]);}
while(x<qx){for(int j=y;j<=yy;j++)del(a[x][j]);x++;}
while(xx<qxx){xx++;for(int j=y;j<=yy;j++)add(a[xx][j]);}
while(xx>qxx){for(int j=y;j<=yy;j++)del(a[xx][j]);xx--;}
while(y>qy){y--;for(int j=x;j<=xx;j++)add(a[j][y]);}
while(y<qy){for(int j=x;j<=xx;j++)del(a[j][y]);y++;}
while(yy<qyy){yy++;for(int j=x;j<=xx;j++)add(a[j][yy]);}
while(yy>qyy){for(int j=x;j<=xx;j++)del(a[j][yy]);yy--;}
//cout<<x<<' '<<y<<' '<<xx<<' '<<yy<<' '<<endl;
ans[q[i].id]=res;
}
for(int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}
C.联盟
我写的是恶心至极的换根……思维直接粗暴,细节稍多。
首先一遍dfs找出$x$的子树中的点到$x$的最长距离$dis[x]$,以及$x$子树中的直径$len[x]$,后者直接维护最长链和次长链就能得到。
然后考虑:如果我们断掉一条边,把树变成两个联通块,那么他们重新相连后的最短直径是?
很显然,联通块1的直径、联通块2的直径、两条直径的二分之一向上取整再相加后+1,这三者取$max$。最后那个的含义就是把两条直径的终点连在一起。
题解的做法是每个点维护子树前缀联通块和后缀联通块的直径端点即可快速合并,但我不太会维护端点,所以直接利用大量的链长信息进行换根dp,每次分类讨论得到结果。这样相当与枚举了每条边断掉后的结果,所以一定能遍历所有情况、找到所有可能断的边。
这个换根的过程要考虑很多东西:父亲到儿子的过程中子树直径已经求出,但外部联通块的直径需要额外维护。这个外部直径可能还是原来的外部直径,也有可能是经过刚刚被扔到外面的父亲的直径。
所以在转移的时候可能要维护最大值、次大值、再次大值。为什么?我们向儿子转移的时候需要除这个儿子的信息之外另外的两个极值来进行比较和拼凑出直径。如果这个儿子就是最大值的提供者,那肯定就不能用最大值来算外部直径了。总之很麻烦,看代码吧。(逃
这样就可以求出前两问的结果。至于第三问,先从第二问结果里随便挑一条边删了,然后取产生的这两个联通块的直径中点即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define pa pair<int,int>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=3e5+5;
int n;
int to[N<<1],head[N],nxt[N<<1],tot=1,id[N<<1],dis[N],len[N];
int s[N<<1],top,ans=0x3f3f3f3f,del;
pa e[N<<1];
void add(int x,int y,int i)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
id[tot]=i;
}
void dfs1(int x,int f)
{
int maxx=0,secmax=0,num=0;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
dfs1(y,x);
len[x]=max(len[x],len[y]);
dis[x]=max(dis[x],dis[y]+1);
num++;
if(maxx<=dis[y])secmax=maxx,maxx=dis[y];
else if(secmax<dis[y])secmax=dis[y];
}
if(num<=1)len[x]=max(len[x],maxx+secmax+num);
else len[x]=max(len[x],maxx+secmax+2);
}
void dfs2(int x,int f,int l1,int l2,int p)
{
int nowmax=max(max(len[x],l1),1+(len[x]+1)/2+(l1+1)/2);
int num=0;
if(nowmax<ans)top=1,s[top]=p,ans=nowmax;
else if(nowmax==ans)s[++top]=p;
int maxx=0,secmax=0,thdmax=0,maxl=0,secmaxl=0;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
if(dis[y]>=maxx)thdmax=secmax,secmax=maxx,maxx=dis[y];
else if(dis[y]>=secmax)thdmax=secmax,secmax=dis[y];
else if(dis[y]>thdmax)thdmax=dis[y];
if(len[y]>=maxl)secmaxl=maxl,maxl=len[y];
else if(len[y]>secmaxl)secmaxl=len[y];
num++;
}
int m1,m2,m3,nowl,nl1,nl2;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
if(maxx==dis[y])m1=secmax,m2=thdmax;
else if(secmax==dis[y])m1=maxx,m2=thdmax;
else m1=maxx,m2=secmax;
if(maxl==len[y])m3=secmaxl;
else m3=maxl;
nowl=0;
if(num>1)nowl=max(nowl,l2+m1+1);
else nowl=max(nowl,l2);
if(num>2)nowl=max(nowl,m1+m2+2);
nl1=max(l1,max(m3,nowl));nl2=l2+1;
if(num>1)nl2=max(nl2,m1+2);
dfs2(y,x,nl1,nl2,id[i]);
}
}
int S,maxd,pre[N],T;
vector<int> link;
void dfs3(int x,int f,int dep)
{
if(dep>maxd)maxd=dep,S=x;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f||del==id[i])continue;
dfs3(y,x,dep+1);
}
}
void dfs4(int x,int f,int dep)
{
if(dep>maxd)maxd=dep,T=x;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f||del==id[i])continue;
pre[y]=x;
dfs4(y,x,dep+1);
}
}
void getdis(int x)
{
link.clear();
S=x;maxd=0;
dfs3(x,0,1);
maxd=0;
for(int i=1;i<=n;i++)
pre[i]=0;
dfs4(S,0,1);
int now=T;
while(now)link.push_back(now),now=pre[now];
} int main()
{
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y,i);add(y,x,i);
e[i]=make_pair(x,y);
}
dfs1(1,0);dfs2(1,0,0,0,0);
cout<<ans<<endl;
sort(s+1,s+top+1);
cout<<top<<' ';
for(int i=1;i<=top;i++)
cout<<s[i]<<' ';
putchar('\n');
del=s[top];
int node1=e[del].first,node2=e[del].second,node3,node4;
getdis(node1);
int sz=link.size();
node3=link[sz/2];
getdis(node2);
sz=link.size();
node4=link[sz/2];
cout<<node1<<' '<<node2<<' '<<node3<<' '<<node4<<endl;
return 0;
}
[CSP-S模拟测试50]反思+题解的更多相关文章
- csps模拟测试50反思
又考崩了,T1一眼秒掉错误思路,然后迅速码完,并码完错误暴力,对拍拍上,以为AC.T2想到了二维莫队,发现是子任务就没去打,一直在想别的,T3最后想到60分打法,没有打完,也没时间暴力,挂掉.T2还有 ...
- [CSP-S模拟测试48]反思+题解
状态很垃圾的一场考试.感觉“这么多分就够了”的心态很是在给自己拖后腿. 打开题面,第一页赫然写着:$Claris' Contest$. 吓得我差点手一抖关掉.不过后来想想似乎强到变态的人出的题都不是很 ...
- [CSP-S模拟测试47]反思+题解
打开题面,T3似乎被换过了.(那我就更有理由直接弃掉了) T1是我最害怕的乱搞题,赶紧扔了看T2.发现是个sb板子?雨天的尾巴弱化版? 然而线段树合并早忘干净了(最近几道可以线段树合并的题都是用别的方 ...
- [NOIP模拟测试37]反思+题解
一定要分析清楚复杂度再打!!!窝再也不要花2h20min用暴力对拍暴力啦!!! 雨露均沾(滑稽),尽量避免孤注一掷.先把暴力分拿全再回来刚正解. 即使剩下的时间不多了也优先考虑认真读题+打暴力而非乱搞 ...
- [NOIP模拟测试34]反思+题解
不要陷入思维定势,如果长时间没有突破就要考虑更改大方向. 不要把简单问题复杂化. 做完的题就先放下,不管能拿多少分.不能过一段时间就回来调一下. $Solutions:$ A.次芝麻 因为$n+m$始 ...
- [NOIP模拟测试32]反思+题解
又考挂了QAQ 总rank直接滑出前20 晚上考试脑子还算比较清醒,可惜都用来xjb乱想错误思路了. T1一眼推柿子,然而并没有头绪所以先码了个暴力.然后…… 一个垃圾暴力我调了1h,大概解决了两位数 ...
- 2019.8.14 NOIP模拟测试21 反思总结
模拟测试20的还没改完先咕着 各种细节问题=错失190pts T1大约三分钟搞出了式子,迅速码完,T2写了一半的时候怕最后被卡评测滚去交了,然后右端点没有初始化为n…但是这样还有80pts,而我后来还 ...
- 2019.8.1 NOIP模拟测试11 反思总结
延迟了一天来补一个反思总结 急匆匆赶回来考试,我们这边大家的状态都稍微有一点差,不过最后的成绩总体来看好像还不错XD 其实这次拿分的大都是暴力[?],除了某些专注于某道题的人以及远程爆踩我们的某学车神 ...
- 2019.8.9 NOIP模拟测试15 反思总结
日常爆炸,考得一次比一次差XD 可能还是被身体拖慢了学习的进度吧,虽然按理来说没有影响.大家听的我也听过,大家学的我也没有缺勤多少次. 那么果然还是能力问题吗……? 虽然不愿意承认,但显然就是这样.对 ...
随机推荐
- 怎么查看keras 或者 tensorflow 正在使用的GPU
查看keras认得到的GPU from keras import backend as K K.tensorflow_backend._get_available_gpus() Out[28]: [' ...
- [LeetCode] 421. Maximum XOR of Two Numbers in an Array(位操作)
传送门 Description Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231. Fin ...
- 什么是php工厂模式
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式.著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见.今天我们就为大家介绍一下PHP中的 ...
- 自定义ThreadLocal和事务(基于自定义AOP)
参考<架构探险--从零开始写javaweb框架>4.6章节 自定义ThreadLocal package smart; import java.util.Collections; impo ...
- 在js里的ejs模板引擎使用
1.首先需要在页面里引用ejs.min.js. 2.将你的模板使用ejs编写,并存成后缀名.stmpl;(可能需要在打包工具里做些处理) 3.在js里使用require引入xxx.stmpl: con ...
- android测试开发概念
一:测试分类 1.分类概览 按测试阶段划分: 单元测试 集成测试 系统测试 验收测试 按是否覆盖源代码: 黑盒测试: 功能测试: 界面测试 逻辑测试 安装测试 应用性测试 兼容性测试 性能测试: 稳定 ...
- 【洛谷p2089】 烤鸡
烤鸡[题目链接] 感觉我超废 关于算法:有很多很多算法吧,但自我感觉最重要的是递归的算法: SOLUTION: 首先忍不住要吐槽这个神仙数据: 嗯???定睛一看,它这数据范围莫不是白瞎了,因为每种配料 ...
- [Codeforces 163D]Large Refrigerator (DFS+剪枝)
[Codeforces 163D]Large Refrigerator (DFS+剪枝) 题面 已知一个长方体的体积为V,三边长a,b,c均为正整数,求长方体的最小表面积S V以质因数分解的形式给出 ...
- 1.openshift搭建
第1章 主机规划和所需文件 1.1 主机规划 IP地址 域名 用途 11.11.233.125 master01.song.test.cnpc 容器编排.etcd 11.11.233.126 mast ...
- 双指针---有序数组的TWO SUM
双指针思想,主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务. 有序数组的 Two Sum Leetcode :167. Two Sum II - Input array is sort ...