前言

昨天说好不考试来着,昨晚就晚睡颓了一会,今天遭报应了,也没好好考,考得挺烂的就不多说了。

T1 string

解题思路

比赛上第一想法就是打一发sort,直接暴力,然后完美TLE40pts,这一部分分也是所有人都拿到了,没什么意义。。

正解是线段树,主流打法有两种:

  1. 开26棵线段树,分别对26种字母进行维护。
  2. 只开一棵线段树,对于区间维护求值。

我当然是选择码量较小并且快的第二种了QAQ,但是聪明睿智的@WindZR选择了第一种打法(code),虽然他多加了好几个inline快了2s才过。

对于每一个线段树区间,如果这个区间都为一个字母,存储。否则,就为0.

在更改的时候,先求一下本区间中各个字母的多少,然后分别对于线段树中的每个字母更改,直接存延迟标记到数值上就好了。

最后统一push_down一下,输出就好了。代码实现可能有一点考验码力。。

code



#include<bits/stdc++.h>
#define ls x<<1
#define rs x<<1|1
using namespace std;
const int N=1e5+10;
int tre[N<<2];
int n,m,len[27];
char s[N];
inline void push_down(int x)
{
if(tre[x])
tre[ls]=tre[rs]=tre[x];
tre[x]=0;
}
inline void push_up(int x)
{
tre[x]=tre[ls]*(tre[ls]==tre[rs]);
}
inline void build(int x,int l,int r)
{
// cout<<x<<' '<<l<<' '<<r<<endl;
if(l==r)
{
tre[x]=s[l]-'a'+1;
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(x);
}
inline void query(int x,int l,int r,int L,int R)
{
// cout<<x<<' '<<l<<' '<<r<<' '<<L<<' '<<R<<endl;
if(L>R||r<L||l>R)
return ;
if(L<=l&&r<=R&&tre[x])
{
len[tre[x]-1]+=r-l+1;
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid)
query(ls,l,mid,L,R);
if(R>mid)
query(rs,mid+1,r,L,R);
// push_up(x);
}
inline void update(int x,int l,int r,int L,int R,int dat)
{
// cout<<x<<' '<<l<<' '<<r<<' '<<L<<' '<<R<<' '<<dat<<endl;
if(L>R||r<L||l>R)
return ;
if(L<=l&&r<=R||tre[x]==dat)
{
tre[x]=dat;
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid)
update(ls,l,mid,L,R,dat);
if(R>mid)
update(rs,mid+1,r,L,R,dat);
push_up(x);
}
inline void Push_down(int x,int l,int r)
{
if(tre[x])
{
for(int i=l;i<=r;i++)
s[i]=tre[x]+'a'-1;
return ;
}
int mid=(l+r)>>1;
Push_down(ls,l,mid);
Push_down(rs,mid+1,r);
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
build(1,1,n);
for(int i=1,x,y,vis;i<=m;i++)
{
memset(len,0,sizeof(len));
scanf("%d%d%d",&x,&y,&vis);
query(1,1,n,x,y);
/*for(int j=0;j<26;j++)
cout<<len[j]<<' ';
cout<<endl;*/
if(vis)
{
int temp=x;
for(int j=0;j<26;j++)
{
if(len[j])
update(1,1,n,temp,temp+len[j]-1,j+1);
temp+=len[j];
}
}
else
{
int temp=x;
for(int j=25;j>=0;j--)
{
if(len[j])
update(1,1,n,temp,temp+len[j]-1,j+1);
temp+=len[j];
}
}
}
Push_down(1,1,n);
for(int i=1;i<=n;i++)
printf("%c",s[i]);
return 0;
}

解题思路

说实话看这题第一眼,我想的是排列组合,后来想了想是个\(n^2\)的dp,但是我想的是枚举坐标,所以整不出来了,在然后就直接搜呗(逃。

这个题正解特别妙,\(f[i][j]\) ,i表示第几列,j表示第i列后面1的个数,最重要的一点是这个题是逆推的。由\((0,0)\)开始,且该位置方案数为1。然后就开始愉快地实现了。

第一部分

显然可以从i-1列转移过来,状态有两种:放与不放,也就是j-1或者 j,利用r总数进行更新。

第二部分

枚举前一行到当前行的可行的左侧数量,再进行枚举左侧放的棋子数然后再乘上一个方案数就好了。

  • 优化:l和r边界的总数用前缀和优化。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10,M=4e3+10,mod=998244353;
int n,m,sl[N],sr[N],f[M][M];
#undef int
int main()
{
#define int register long long
#define ll long long
scanf("%lld%lld",&n,&m);
for(int i=1,l,r;i<=n;i++)
{
scanf("%lld%lld",&l,&r);
sl[l]++;
sr[r]++;
}
for(int i=1;i<=m;i++)
{
sl[i]+=sl[i-1];
sr[i]+=sr[i-1];
// cout<<sl[i]<<' '<<sr[i]<<endl;
}
f[0][0]=1;
for(int i=1;i<=m;i++)
{
f[i][0]=f[i-1][0];
for(int j=1;j<=i;j++)
f[i][j]=(f[i-1][j]+f[i-1][j-1]*(sr[i]-j+1)%mod)%mod;
for(int j=sl[i-1];j<sl[i];j++)
for(int k=0;k<=i;k++)
f[i][k]=f[i][k]*(i-j-k)%mod;
}
printf("%lld",f[m][n]);
return 0;
}

T2 matrix

解题思路

说实话看这题第一眼,我想的是排列组合,后来想了想是个\(n^2\)的dp,但是我想的是枚举坐标,所以整不出来了,在然后就直接搜呗(逃。

这个题正解特别妙,\(f[i][j]\) ,i表示第几列,j表示第i列后面1的个数,最重要的一点是这个题是逆推的。由\((0,0)\)开始,且该位置方案数为1。然后就开始愉快地实现了。

第一部分

显然可以从i-1列转移过来,状态有两种:放与不放,也就是j-1或者 j,利用r总数进行更新。

第二部分

枚举前一行到当前行的可行的左侧数量,再进行枚举左侧放的棋子数然后再乘上一个方案数就好了。

  • 优化:l和r边界的总数用前缀和优化。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10,M=4e3+10,mod=998244353;
int n,m,sl[N],sr[N],f[M][M];
#undef int
int main()
{
#define int register long long
#define ll long long
scanf("%lld%lld",&n,&m);
for(int i=1,l,r;i<=n;i++)
{
scanf("%lld%lld",&l,&r);
sl[l]++;
sr[r]++;
}
for(int i=1;i<=m;i++)
{
sl[i]+=sl[i-1];
sr[i]+=sr[i-1];
// cout<<sl[i]<<' '<<sr[i]<<endl;
}
f[0][0]=1;
for(int i=1;i<=m;i++)
{
f[i][0]=f[i-1][0];
for(int j=1;j<=i;j++)
f[i][j]=(f[i-1][j]+f[i-1][j-1]*(sr[i]-j+1)%mod)%mod;
for(int j=sl[i-1];j<sl[i];j++)
for(int k=0;k<=i;k++)
f[i][k]=f[i][k]*(i-j-k)%mod;
}
printf("%lld",f[m][n]);
return 0;
}

T3 big

解题思路

考试的时候,我一开始都没打算打这个题(还是我太菜了),联想到了博弈论这一类奇奇怪怪的东西。。。后来仔细看了看题目中操作的式子,想到了循环节,也想到了分位运算,但是就是没有想到用Tire树 [流泪].jpg

\((\lfloor\dfrac{2x}{2^n}\rfloor+2x) \bmod 2^n\),这个式子一看就不是瞎整的,有二进制的感觉,\(\lfloor\dfrac{2x}{2^n}\rfloor\) 就是\((x>>(n-1))\)(取第n位)与\(x<<1\)的加和再按位与上\(1<<n\),扣下前n位,总的来看就类似与把110101变成了101011。

我们再求一个一个前缀和后缀,后缀用于维护u对手操作完没变的点,前缀维护操作过的点,不难发现,对于i之前的所有的都操作与将i之前的异或再操作结果是一样的。

可以手动模拟一下。

然后就是构建Tire树,从根开始往下遍历每一位,处理情况有3种

  1. 子节点0和1都有:此时如果我选择0,那么对手就会选择1,同样的,如果我选择1,那么对手就会选择0,因此这个点对于答案是没有贡献的,只需要继承上来就好了。
  2. 只有0节点:只可以走这一路,对答案有贡献\(1<<res\)
  3. 只有1节点:与只有0节点的情况大同小异,贡献也是\(1<<res\)
  • 优化:遍历时可以返回一个pair同时储存两个答案。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,m,s[N],suf[N],he[N];
int cnt=1,tre[N*40][2];
int work(int x)
{
// return 2*x/(1<<n)+2*x%(1<<n);
return ((x>>(n-1))+(x<<1))&((1<<n)-1);
}
inline void Tire_Add(int x)
{
int now=1;
for(int i=n-1;i>=0;i--)
{
bool p=x&(1<<i);
if(!tre[now][p])
tre[now][p]=++cnt;
now=tre[now][p];
// cout<<now<<' ';
}
}
pair<int,int> dfs(int now,int res)
{
if(res==-1)
return make_pair(0,1);
if(tre[now][0]&&tre[now][1])
{
pair<int,int> x=dfs(tre[now][0],res-1),y=dfs(tre[now][1],res-1);
if(x.first==y.first)
return make_pair(x.first,x.second+y.second);
if(x.first>y.first)
return x;
return y;
}
if(tre[now][0])
{
pair<int,int> x=dfs(tre[now][0],res-1);
x.first+=(1<<res);
return x;
}
if(tre[now][1])
{
pair<int,int> x=dfs(tre[now][1],res-1);
x.first+=(1<<res);
return x;
}
}
#undef int
int main()
{
#define int long long
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
scanf("%lld",&s[i]);
for(int i=m;i>=1;i--)
{
suf[i]=suf[i+1]^s[i];
// cout<<suf[i]<<' ';
}
for(int i=1;i<=m;i++)
{
he[i]=work(s[i])^he[i-1];
// cout<<he[i]<<' ';
}
for(int i=0;i<=m;i++)
Tire_Add(he[i]^suf[i+1]);
pair<int,int> ans=dfs(1,n-1);
printf("%lld\n%lld",ans.first,ans.second);
return 0;
}

T4 P2403 [SDOI2010]所驼门王的宝藏

解题思路

算是这次比赛里比较好想的一个题了 (虽然我打的裸暴力)。

正解的话就是Tarjan缩点+DAG

主流打法有两种:

  1. 建假点,利用假点运算
  2. sort排序,暴力建边 (我当然是喜欢暴力的了)

对于自动排序这一类的问题,hash和map都可以做,(我当然是选择暴力的map了)题面里的所谓各种门,无非是连接各个点的一条边,显然,对于一系列的点如果我们可以到达其中的一个就一定可以全部到达,这不就是强联通分量吗。

因此我们对于每一种门分别查找建出边来,

建边

横天门与纵寰门

这两种门是大同小异的,这里举横天门的例子,先将所有的宝藏室排个序(第一关键字是横坐标,第二关键字是操作,第三关键字是总纵坐标)。

  • 优化:操作的关键字,先判y的比先判x的快了一半(先判x也没事 ~毕竟洛谷数据水嘛)。

任意门

直接暴力枚举各个点建出边来就好了,分别扫一下八个方向是否有点就好了。

其他操作

接下来就可以套Tarjan的板子缩点 了,顺便储存一下各个点属于那一个强联通分量,处理入度为拓扑排序做准备,然后再进行dfs拓扑排序,求出最大值就ok了。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,t,ans,du[N],f[N];
int tot,head[N],ver[N*100],nxt[N*100],fro[N*100];
int cnt,tim,dfn[N],low[N],bel[N],sum[N];
int top,sta[N];
bool bosta[N];
map<int,bool> vis[N];
map<int,int> s[N];
map<int,bool>::iterator it;
int sx[9]={0,-1,-1,-1,0,0,1,1,1};
int sy[9]={0,-1,0,1,-1,1,-1,0,1};
struct Ques
{
int x,y,opt,id;
}q[N];
inline bool comp1(Ques x,Ques y)
{
if(x.x!=y.x)
return x.x<y.x;
if(y.opt==1)
return false;
if(x.opt==1)
return true;
return x.y<y.y;
}
inline bool comp2(Ques x,Ques y)
{
if(x.y!=y.y)
return x.y<y.y;
if(y.opt==2)
return false;
if(x.opt==2)
return true;
return x.x<y.x;
}
inline void add_edge(int x,int y)
{
ver[++tot]=y;
fro[tot]=x;
nxt[tot]=head[x];
head[x]=tot;
}
inline void Tarjan(int x)
{
dfn[x]=low[x]=++tim;
sta[++top]=x;
bosta[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(!dfn[to])
{
Tarjan(to);
low[x]=min(low[x],low[to]);
}
else if(!bel[to])
low[x]=min(low[x],low[to]);
}
if(dfn[x]==low[x])
{
cnt++;
int temp=sta[top--];
bel[temp]=cnt;
sum[cnt]++;
bosta[temp]=false;
while(temp!=x)
{
temp=sta[top--];
bel[temp]=cnt;
sum[cnt]++;
bosta[temp]=false;
}
}
}
inline void dfs(int x,int fa)
{
if(f[x]>sum[x])
return ;
f[x]=sum[x];
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa)
continue;
dfs(to,x);
f[x]=max(f[x],f[to]+sum[x]);
}
}
inline int read()
{
char ch=getchar();
int x=0;
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
int main()
{
// scanf("%d%d%d",&t,&n,&m);
#define int register int
t=read();
n=read();
m=read();
for(int i=1;i<=t;i++)
{
// scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].opt);
q[i].x=read();
q[i].y=read();
q[i].opt=read();
q[i].id=i;
s[q[i].x][q[i].y]=i;
}
#define head wzrdsb
int head=1,tail=1;
sort(q+1,q+t+1,comp1);
for(int i=1;i<=t;i++)
{
if(q[i].x!=q[i+1].x)
{
if(head!=tail)
add_edge(q[tail].id,q[head].id);
tail=head=i+1;
}
else
{
if(q[tail].opt==1)
add_edge(q[tail].id,q[i+1].id);
if(q[i+1].opt==1)
tail=i+1;
if(q[head].opt!=1)
tail=head=i+1;
}
}
head=1;
tail=1;
sort(q+1,q+t+1,comp2);
for(int i=1;i<=t;i++)
{
if(q[i].y!=q[i+1].y)
{
if(head!=tail)
add_edge(q[tail].id,q[head].id);
tail=head=i+1;
}
else
{
if(q[tail].opt==2)
add_edge(q[tail].id,q[i+1].id);
if(q[i+1].opt==2)
tail=i+1;
if(q[head].opt!=2)
tail=head=i+1;
}
}
#undef head
for(int i=1;i<=t;i++)
if(q[i].opt==3)
{
for(int j=1;j<=8;j++)
if(s[q[i].x+sx[j]].count(q[i].y+sy[j]))
add_edge(q[i].id,s[q[i].x+sx[j]][q[i].y+sy[j]]);
}
for(int i=1;i<=t;i++)
if(!dfn[i])
Tarjan(i);
for(int i=1;i<=tot;i++)
{
int to=ver[i];
if(bel[fro[i]]!=bel[to])
vis[bel[fro[i]]][bel[to]]=true;
}
memset(head,0,sizeof(head));
tot=0;
for(int i=1;i<N;i++)
for(it=vis[i].begin();it!=vis[i].end();it++)
{
add_edge(i,it->first);
du[it->first]++;
}
for(int i=1;i<=cnt;i++)
if(!du[i])
{
dfs(i,0);
ans=max(ans,f[i]);
}
printf("%d",ans);
return 0;
}

6.7考试总结(NOIP模拟5)的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. 「考试」noip模拟9,11,13

    9.1 辣鸡 可以把答案分成 每个矩形内部连线 和 矩形之间的连线 两部分 前半部分即为\(2(w-1)(h-1)\),后半部分可以模拟求(就是讨论四种相邻的情况) 如果\(n^2\)选择暴力模拟是有 ...

  7. 6.11考试总结(NOIP模拟7)

    背景 时间分配与得分成反比,T1 20min 73pts,T2 1h 30pts,T3 2h 15pts(没有更新tot值,本来应该是40pts的,算是本次考试中最遗憾的地方了吧),改起来就是T3比较 ...

  8. 6.10考试总结(NOIP模拟6)

    前言 就这题考的不咋样果然还挺难改的.. T1 辣鸡 前言 我做梦都没想到这题正解是模拟,打模拟赛的时候看错题面以为是\(n\times n\)的矩阵,喜提0pts. 解题思路 氢键的数量计算起来无非 ...

  9. [考试反思]NOIP模拟测试19:洗礼

    []260 []230[]210 []200[8]170[9]160 这套题一般,数据很弱,T1T2暴力都能A,而且都是一些思维题,想不到就爆0. 原因不明,很多一直很强的人在这一次滑铁卢了,于是我个 ...

随机推荐

  1. 【opencv】VideoCapture打不开本地视频文件或者网络IP摄像头

    1.前提:成功打开本地USB摄像头 // 创建VideoCapture对象 VideoCapture vc = new VideoCapture(); // 可以成功打开本地USB摄像头 // 参数可 ...

  2. layui中时间插件laydate的使用

    1.加载layui.js 2.html部分 <div class="layui-inline"> <label class="layui-form-la ...

  3. 【实用小技巧】spring与springmvc自动扫描包重复的问题解决

    spring对应配置文件为: <!-- 配置自动扫描的包,此时要排除Controller --> <context:component-scan base-package=" ...

  4. (原创)高DPI适配经验系列:(三)字体与字号、缩放锚点

    一.前言 程序最基本的元素,就是文本,也就是字体.如果程序未进行高DPI的适配,最直观的感受便是字体的模糊.所以本篇便来说一下高DPI适配中的字体问题. 高DPI的适配,简单来说便是便是根据不同的DP ...

  5. Docker系列——Grafana+Prometheus+Node-exporter服务器监控平台(一)

    在最近的博文中,都是介绍监控平台的搭建,其实并不难,主要是需要自己动手操作,实践一番就会了. 有天在想,云上的服务器,是不是也可以搭建一个监控平台,所以就捣鼓了一下,不过遗憾的是,使用阿里云开源的插件 ...

  6. 036.Python的TCP语法

    TCP语法 1 建立一个socket对象 import socket sk = socket.socket() print (sk) 执行 [root@node10 python]# python3 ...

  7. systemd 进程管理详解

    systemd进程管理 systemd管理的优势 1.最新系统都采用systemd管理(RedHat7,CentOS7,Ubuntu15...) 2.CentOS7 支持开机并行启动服务,显著提高开机 ...

  8. Scala 中的可变(var)与不可变(val)

    引言 Scala 中定义变量分为 var(可变变量)和 val(不可变变量) Scala 中集合框架也分为可变集合和不可变集合.比如 List(列表) 和 Tuple(元组)本身就是不可变的,set ...

  9. Canal--服务端和客户端搭建

    服务端 源码编译 git clone https://github.com/alibaba/canal.git 用IDEA打开克隆的项目 等待maven下载完依赖后开始编译过程 依赖下载完成后,打开m ...

  10. 9.10 nohup:用户退出系统进程继续工作

    nohup命令 可以将程序以忽略挂起信号的方式运行起来,被运行程序的输出信息将不会显示到终端.        无论是否将nohup命令的输出重定向到终端,输出都将写入到当前目录的nohup.out文件 ...