7.29考试总结(NOIP模拟27)[牛半仙的妹子图·Tree·序列]
前言
从思路上来讲是比较成功的,从分数上就比较令人失望了。
考场上是想到了前两个题的正解思路,其实最后一个题是半个原题,只可惜是我看不懂题。。。
这波呀,这波又是 语文素养限制OI水平。。
改题的时候连官方题解都没看一眼就码过了,感觉不错。
总感觉出题人的题目名字有点。。。(T2的wrxcsd是啥意思????)
T1 牛半仙的妹子图
解题思路
做法有很多,比如什么:最小生成树,魔改拓扑排序,等等。
我的做法是 Dij 最短路,看题目第一眼就是某个点到所有点路径上困难值最大值的最小值。
跑一个 Dij 之后直接对于每一个距离离散化一下,进而求出当每种最大的可以克服的困难度可以到达的种类数。
观察到问题是一个区间的值,线段树就算了,毕竟我们不需要修改。
考虑前缀和,查找两个端点在离散化数组里的值,对于整个的部分直接前缀和处理。
对于剩余的部分用剩余部分的区间长度乘上贡献即可。
这种打法对于 \(l=r\) 似乎是不可做的,因此直接特判就行了(逃。
可惜我考场上一时糊涂,把离线和在线分开处理了,然后离线错了,还没特判,\(100pts\rightarrow 55pts\)
本来以为切了的,我裂开。。。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=2e6+10,M=6e3+10;
int n,m,ans,fro,T,opt,mod,dis[N],all[N],qzh[N],pre[N];
int tot,head[N],ver[N<<1],edge[N<<1],nxt[N<<1];
int cnt,lsh[N<<2];
bool vis[N];
priority_queue<pair<int,int> > que;
struct Node
{
int dis,col;
}s[N];
struct Ques
{
int l,r;
}q[N];
bool comp(Node x,Node y)
{
return x.dis<y.dis;
}
void add_edge(int x,int y,int val)
{
ver[++tot]=y;
edge[tot]=val;
nxt[tot]=head[x];
head[x]=tot;
}
void Dij()
{
que.push(make_pair(0,fro));
memset(dis,0x3f,sizeof(dis));
dis[fro]=0;
while(!que.empty())
{
int x=que.top().second;
que.pop();
if(vis[x]) continue;
vis[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i],val=edge[i];
if(dis[to]>max(dis[x],val))
{
dis[to]=max(dis[x],val);
que.push(make_pair(-dis[to],to));
}
}
}
}
signed main()
{
n=read();
m=read();
T=read();
fro=read();
opt=read();
if(opt) mod=read();
for(int i=1;i<=n;i++)
s[i].col=read();
for(int i=1,x,y,val;i<=m;i++)
{
x=read();
y=read();
val=read();
add_edge(x,y,val);
add_edge(y,x,val);
}
for(int i=1;i<=T;i++)
{
q[i].l=read();
q[i].r=read();
}
Dij();
for(int i=1;i<=n;i++)
lsh[++cnt]=s[i].dis=dis[i];
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(int i=1;i<=n;i++)
s[i].dis=lower_bound(lsh+1,lsh+cnt+1,s[i].dis)-lsh;
sort(s+1,s+n+1,comp);
for(int i=1;i<=n;i++)
{
all[s[i].col]++;
int temp=0;
if(all[s[i].col]==1) temp=1;
qzh[s[i].dis]=qzh[s[i-1].dis]+temp;
}
for(int i=1;i<=cnt;i++)
pre[i]=pre[i-1]+qzh[i-1]*(lsh[i]-lsh[i-1]-1)+qzh[i];
for(int i=1,li,ri;i<=T;i++)
{
li=q[i].l;
ri=q[i].r;
if(opt)
{
li=(li^ans)%mod+1;
ri=(ri^ans)%mod+1;
if(li>ri) swap(li,ri);
}
if(li!=ri)
{
int ls=upper_bound(lsh+1,lsh+cnt+1,li)-lsh;
int rs=upper_bound(lsh+1,lsh+cnt+1,ri)-lsh;
ans=pre[rs-1]-pre[ls]+qzh[ls];
ans+=(lsh[ls]-li)*qzh[ls-1]+(ri-lsh[rs-1])*qzh[rs-1];
}
else
{
if(li<lsh[cnt])
{
int temp=upper_bound(lsh+1,lsh+cnt+1,li)-lsh-1;
if(temp<=0) ans=0;
else ans=qzh[temp];
}
else ans=qzh[cnt];
}
printf("%lld\n",ans);
}
return 0;
}
T2 牛半仙的妹子Tree
解题思路
反正我这个法是个暴力,但是能切题就行。
题库数据是可以直接打过去,但是 yspm 和 zero4338 造了两组数据,成功 Hack 掉了我。。
我又看了一下数据,诶,又是链,我直接来一套 “组合拳”,其实就是对于不同的采用不同的打法。
主要思路
这个可以切掉除了Hack数据之外的点,但是优化之前容易被各种链给卡掉(优化思想来自 战神 )
首先,对于每一个开始不喜欢的点(以后简称为插入)其实需要的只是他插入的时间和位置。
因此我们开一个数组记录下这两个值。
然后在每次查询的时候暴力枚举数组里每一个点与现在查询的点的距离与时间差的关系。
距离可以用 RMQ 或者 树链剖分 来搞,理论上来讲 RMQ 是更快的。
但是实际上,因为 RMQ 初始化时间比较长,所以还是 树链剖分 可能更快一些。
这样的预估分数是 \(40pts\sim 60pts\) 但是出乎意料地搞到了 90pts!!。
优化插入部分,我们发现其实有些节点可以传到的点是可以在别的节点也传到的。
那么我们就没有必要把它给插进去,因此,在每一次插入之前进行一次类似于查询的操作就好了。
特殊判断思路
卡掉上面的做法只需要让我们的存储数组里的元素足够多,并且每一次查询都查询最远的两个端点就好了。
因此,WindZR 想出了这样一种做法,适用于所有的类似于链(一个深度十分大的主干上有一些小的分支)
其实就是优化后的扩散。。。
发现每个点有用的扩散只有一次,因此我们记录上一次数组里扩散到的位置。
然后再此基础上接着扩散一个表示每一个是否被扩散到就好了。
清空操作直接清空数组就好了。
其它
这两种做法(不要在意代码里的\(\dfrac{5}{8}\),本来想卡个边界的但是好像不卡也行)的时间复杂度都有一点玄学(至少我是这么想的),我最快卡到了 500ms+(数据加强后)

当然也有强者卡进了500ms以内,但毕竟是我的做法大众普及了。。。
也不知道为啥机房里有不少人都在 Hack 我。。。。。
好像这两种做法的结合也可以被卡掉,可以搞一个类似于神经元的图(其实就是链+菊花图)。
或者一种长菊花图(就是让多条链的端点连接到一个节点上)(可fengwu说卡不掉,有异议的请线下解决)
但是这种数据有点难造,所以欢迎各位前来 Hack 。
另外,yspm的线段树做法或者其他的点分治好像也可以。。。
code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10;
int n,m,las,top;
int tot,head[N],nxt[N<<1],ver[N<<1];
int tim,dep[N],topp[N],siz[N],dfn[N],son[N],fa[N];
int cnt,st[N],mxdep;
bool flag,vis[N];
struct Node
{
int pos,tim;
}sta[N];
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x)
{
mxdep=max(mxdep,dep[x]);
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
fa[to]=x;
dep[to]=dep[x]+1;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
int dist(int x,int y)
{
return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
void Special_Judge()
{
for(int i=1,opt,x;i<=m;i++)
{
opt=read();
x=read();
int pre=cnt;
for(int k=las+1;k<=pre;k++)
for(int j=head[st[k]];j;j=nxt[j])
{
if(!vis[ver[j]]) st[++cnt]=ver[j];
vis[ver[j]]=true;
}
las=pre;
if(opt==2)
{
memset(vis,false,sizeof(vis));
cnt=las=0;
}
else if(opt==1)
{
if(!vis[x]) st[++cnt]=x,vis[x]=true;
}
else
{
if(vis[x]) printf("wrxcsd\n");
else printf("orzFsYo\n");
}
}
exit(0);
}
signed main()
{
n=read();
m=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add_edge(x,y);
add_edge(y,x);
if(x!=y+1&&y!=x+1) flag=true;
}
dfs1(1);
dfs2(1,1);
if(!flag||mxdep>=n*5/8) Special_Judge();
for(int i=1,opt,x;i<=m;i++)
{
opt=read();
x=read();
if(opt==2) top=0;
else if(opt==1)
{
bool jud=true;
for(int j=1;j<=top;j++)
if(dist(sta[j].pos,x)<=i-sta[j].tim)
{
jud=false;
break;
}
if(jud) sta[++top]=(Node){x,i};
}
else
{
bool jud=false;
for(int j=1;j<=top;j++)
if(dist(sta[j].pos,x)<=i-sta[j].tim)
{
jud=true;
break;
}
if(jud) printf("wrxcsd\n");
else printf("orzFsYo\n");
}
}
return 0;
}
T3 牛半仙的妹子序列
解题思路
其实就是极长上升序列。
和之前做的God Kowns几乎是一个题,还是李超线段树或者线段树维护单调栈。。
今天刚打了疫苗有点困,可能口胡说不清,所以不懂的请移步 God Kowns
code
#include<bits/stdc++.h>
#define int long long
#define ls x<<1
#define rs x<<1|1
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=2e5+10,mod=998244353;
int n,mx,s[N],q[N<<2];
struct Segment_Tree
{
int mx,sum;
}tre[N<<2];
int work(int x,int l,int r,int num)
{
if(l==r)
{
if(tre[x].mx>num) return tre[x].sum%mod;
return 0;
}
int mid=(l+r)>>1;
if(tre[rs].mx>num) return (q[ls]+work(rs,mid+1,r,num)%mod)%mod;
return work(ls,l,mid,num)%mod;
}
int query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
int temp=work(x,l,r,mx);
mx=max(mx,tre[x].mx);
return temp%mod;
}
int mid=(l+r)>>1,sum=0;
if(R>mid) sum=query(rs,mid+1,r,L,R)%mod;
if(L<=mid) sum=(sum+query(ls,l,mid,L,R)%mod)%mod;
return sum%mod;
}
void update(int x,int l,int r,int pos,int num1,int num2)
{
if(l==r)
{
tre[x].sum=q[x]=num1;
tre[x].mx=num2;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(ls,l,mid,pos,num1,num2);
else update(rs,mid+1,r,pos,num1,num2);
tre[x].mx=max(tre[ls].mx,tre[rs].mx);
q[ls]=work(ls,l,mid,tre[rs].mx)%mod;
tre[x].sum=(q[ls]+tre[rs].sum)%mod;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1;i<=n;i++)
{
mx=-1;
int val=query(1,0,n+1,0,s[i]-1)%mod;
if(!val) val=1;
update(1,0,n+1,s[i],val,i);
}
mx=-1;
printf("%lld",query(1,0,n+1,0,n)%mod);
return 0;
}
7.29考试总结(NOIP模拟27)[牛半仙的妹子图·Tree·序列]的更多相关文章
- 2021.7.29考试总结[NOIP模拟27]
T1 牛半仙的妹子图 做法挺多的,可以最小生成树或者最短路,复杂度O(cq),c是颜色数. 我考场上想到了原来做过的一道题影子,就用了并查集,把边权排序后一个个插入,记录权值的前缀和,复杂度mlogm ...
- 2021.6.29考试总结[NOIP模拟10]
T1 入阵曲 二位前缀和暴力n4可以拿60. 观察到维护前缀和时模k意义下余数一样的前缀和相减后一定被k整除,前缀和维护模数,n2枚举行数,n枚举列, 开一个桶记录模数出现个数,每枚举到该模数就加上它 ...
- NOIP 模拟 $27\; \rm 牛半仙的妹子图$
题解 \(by\;zj\varphi\) 颜色数很少,考虑枚举颜色数. 建出来一棵最小生成树,可以证明在最小生成树上,一个点到另一个点的路径上的最大权值最小(易证,考虑 \(\rm kruskal\) ...
- noip模拟27[妹子图·腿·腰](fengwu半仙的妹子们)
\(noip模拟27\;solutions\) 这次吧,我本来以为我能切掉两个题,结果呢??只切掉了一个 不过,隔壁Varuxn也以为能切两个,可惜了,他一个都没切...... 确实他分比我高一点,但 ...
- 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]
6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...
- 5.23考试总结(NOIP模拟2)
5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...
- 5.22考试总结(NOIP模拟1)
5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
- [考试总结]noip模拟23
因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...
随机推荐
- AgileConfig轻量级配置中心1.3.0发布,支持多用户权限控制
AgileConfig 当初是设计给我自己用的一个工具,所以只设置了一道管理员密码,没有用户的概念.但是很多同学在使用过后都提出了需要多用户支持的建议.整个团队或者整个公司都使用同一个密码来管理非常的 ...
- P1045 [NOIP2003 普及组] 麦森数
题目描述 形如2^P−1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2^P−1不一定也是素数. 到1998年底,人们已找到了37个麦森数.最大的一个是P=3021377, ...
- NX二次开发-获取面的外围边和孔槽边
函数: UF_MODL_ask_face_loops() 获取面的所有封闭边组合(多组edge) UF_MODL_ask_loop_list_count() 获取loop的数量(面上孔.槽的数量+1 ...
- 【NX二次开发】批量数字签名的方法,解决自己电脑编译的dll在用户正版NX无法使用的问题
在UG5.0开始,所有开发的DLL都要"签名"后才能被客户端上正版的NX调用. 1. 如果是基于c++开发的dll,使用如下方法可以顺利签名成功(这里借用网上现有的文字和图片) 1 ...
- Redis 性能问题分析
在一些网络服务的系统中,Redis 的性能,可能是比 MySQL 等硬盘数据库的性能更重要的课题.比如微博,把热点微博[1],最新的用户关系,都存储在 Redis 中,大量的查询击中 Redis,而不 ...
- SpringCloud Alibaba实战(7:nacos注册中心管理微服务)
源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 在上一节我们已经完成了Nacos Server的本地部署,这一节我们学习如何将Nac ...
- Qt实现基于多线程的文件传输(服务端,客户端)
1. 效果 先看看效果图 这是传输文件完成的界面 客户端 服务端 2. 知识准备 其实文件传输和聊天室十分相似,只不过一个传输的是文字,一个传输的是文件,而这方面的知识,我已经在前面的博客写过了,不了 ...
- mysql的主从复制延迟问题--看这一篇就够了
在之前我们已经讲解了一主一从,双主双从的mysql集群搭建,在单机应用的时候看起来没有问题,但是在企业的生产环境中,在很多情况下都会有复制延迟的问题. 主从复制的原理我们在此处就不再赘述了,之 ...
- 入门Kubernetes - 滚动升级/回滚
一.前言 上一篇文章中对yaml文件格式进行了解,并对k8s中各种主要资源通过yaml创建时的定义模板.接来下就进一步学习k8s的各种特点.并应用在示例中. 接下来先实现.Net Core Api程序 ...
- 第11章:Pod数据持久化
参考文档:https://kubernetes.io/docs/concepts/storage/volumes/ Kubernetes中的Volume提供了在容器中挂载外部存储的能力 Pod需要设置 ...