前言

从思路上来讲是比较成功的,从分数上就比较令人失望了。

考场上是想到了前两个题的正解思路,其实最后一个题是半个原题,只可惜是我看不懂题。。。

这波呀,这波又是 语文素养限制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·序列]的更多相关文章

  1. 2021.7.29考试总结[NOIP模拟27]

    T1 牛半仙的妹子图 做法挺多的,可以最小生成树或者最短路,复杂度O(cq),c是颜色数. 我考场上想到了原来做过的一道题影子,就用了并查集,把边权排序后一个个插入,记录权值的前缀和,复杂度mlogm ...

  2. 2021.6.29考试总结[NOIP模拟10]

    T1 入阵曲 二位前缀和暴力n4可以拿60. 观察到维护前缀和时模k意义下余数一样的前缀和相减后一定被k整除,前缀和维护模数,n2枚举行数,n枚举列, 开一个桶记录模数出现个数,每枚举到该模数就加上它 ...

  3. NOIP 模拟 $27\; \rm 牛半仙的妹子图$

    题解 \(by\;zj\varphi\) 颜色数很少,考虑枚举颜色数. 建出来一棵最小生成树,可以证明在最小生成树上,一个点到另一个点的路径上的最大权值最小(易证,考虑 \(\rm kruskal\) ...

  4. noip模拟27[妹子图·腿·腰](fengwu半仙的妹子们)

    \(noip模拟27\;solutions\) 这次吧,我本来以为我能切掉两个题,结果呢??只切掉了一个 不过,隔壁Varuxn也以为能切两个,可惜了,他一个都没切...... 确实他分比我高一点,但 ...

  5. 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]

    6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...

  6. 5.23考试总结(NOIP模拟2)

    5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...

  7. 5.22考试总结(NOIP模拟1)

    5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...

  8. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

  9. [考试总结]noip模拟23

    因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...

随机推荐

  1. go语言的排序和搜索(转载)

    http://studygolang.com/articles/1598 go语言的排序和搜索 晚上准备动手写点 go 的程序的时候,想起 go 如何排序的问题.排序 sort 是个基本的操作,当然搜 ...

  2. Java显式锁

    Java 显式锁. 一.显式锁 什么是显式锁? 由自己手动获取锁,然后手动释放的锁. 有了 synchronized(内置锁) 为什么还要 Lock(显示锁)? 使用 synchronized 关键字 ...

  3. Linux系统命令login的翻译

    LOGIN(1)                                用户命令                               LOGIN(1)名称       login - ...

  4. vue根据变量值绑定src的路径

    路径必须用require包裹起来才会起作用

  5. Python3中列表、字典、元组、集合的看法

    文首,我先强调一下我是一个弱鸡码农,这个随笔是在我学习完Python3中的元组.字典.列表,集合这四种常见数据的数据类型的一些感想,如果有什么不对的地方欢迎大家予以指正.谢谢大家啦 回归正题:这篇随笔 ...

  6. Django(68)drf分页器的使用

    前言 当后台返回的数据过多时,我们就要配置分页器,比如一页最多只能展示10条等等,drf中默认配置了3个分页面 PageNumberPagination:基础分页器,性能略差 LimitOffsetP ...

  7. 「10.15」梦境(贪心)·玩具(神仙DP)·飘雪圣域(主席树\树状数组\莫队)

    A. 梦境 没啥可说的原题.... 贪心题的常见套路我们坐标以左端点为第一关键字,右端点为第二关键字 然后对于每个转折点,我们现在将梦境中左端点比他小的区间放进$multiset$里 然后找最近的右端 ...

  8. XAML代码格式化神器扩展:XAML Styler,从安装到放弃

    背景 平时,我们写XAML的时候,写着写着就多了,乱了,听说这个神器扩展可以一键格式化. XAML Styler -- VS格式化扩展 XAML Styler From MarketPlace 安装 ...

  9. P2P技术(2)——NAT穿透

    P2P可以是一种通信模式.一种逻辑网络模型.一种技术.甚至一种理念.在P2P网络中,所有通信节点的地位都是对等的,每个节点都扮演着客户机和服务器双重角色,节点之间通过直接通信实现文件信息.处理器运算能 ...

  10. CRM系统选型时的参考哪些方面

    企业不论在制定营销策略或是在进行CRM系统选型时,首先都是要了解自身的需求.每一家企业的情况和需求都有很大差异,CRM系统的功能也都各有偏重.有些CRM偏重销售管理.有些注重于营销自动化.有些则侧重于 ...