前言

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

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. 百度地图api根据用户IP获取用户位置(PHP)

    1.百度地图开放平台找的你的ak ,链接:http://lbsyun.baidu.com/apiconsole/key 2.获取用户ip地址(外网ip 服务器上可以获取用户外网Ip 本机ip地址只能获 ...

  2. 初窥软件工程 2020BUAA软件工程$\cdot$个人博客作业

    初窥软件工程 2020BUAA软件工程\(\cdot\)个人博客作业 目录 初窥软件工程 2020BUAA软件工程$\cdot$个人博客作业 一.作业要求简介 二.正文 (一) 快速看完整部教材,列出 ...

  3. 《前端运维》一、Linux基础--03Shell基础及补充

    诶诶欸?不是学Linux么?怎么要讲shell了?shell是啥?啥是shell? 别急,我们先简单了解下shell是什么.Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁. ...

  4. Sentinel导航

    简介 最近都在弄微服务的东西,现在来记录下收获.我从一知半解到现在能从0搭建使用最大的感触有两点 1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功 2.网上 ...

  5. Ansible_变量管理与设置

    一.Ansible变量管理 1.变量概述 Ansible支持利用变量来存储值,并在Ansible项目的所有文件中重复使用这些值.这可以简化项目的创建和维护,并减少错误的数量 通过变量,可以轻松地在An ...

  6. linux服务之NFS服务篇

    一.概念 NFS(Network File Server) 网络文件系统(映射).网络文件系统是FreeBSD支持的文件系统中的一种,也被称为NFS: NFS允许一个系统在网络上与他人共享目录和文件. ...

  7. MyBatis 高级查询之一对一查询(九)

    高级查询之一对一查询 查询条件:根据游戏角色ID,查询账号信息 我们在之前创建的映射器接口 GameMapper.java 中添加接口方法,如下: /** * 根据角色ID查询账号信息 * @para ...

  8. STM32电路设计注意

    以后画STM32的电路板 VDDA一定要接 张JF说  VDDA是给内部的时钟电路供电的 还有一定要留 串口或者  下载调试口(串口) 或者显示器接口 输入输出设备最好都留着 这样才能方便调试

  9. Centos7.4 docker安装包下载以及离线安装

    docker安装包下载地址:https://download.docker.com/linux/centos/7/x86_64/stable/Packages/ 需要下载一个selinux包:dock ...

  10. ifconfig显示的网卡信息和我的配置文件名不符

    比如我的配置文件, cd /etc/sysconfig/network-scripts/ifcfg-Auto_eth0是这个名称,但是我使用ifconfig显示的信息却是 eth6   Link en ...