前言

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

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. .NET平台系列5 .NET Core 简介

    系列目录     [已更新最新开发文章,点击查看详细] 自1995年互联网战略日以来最雄心勃勃的事业 -- 微软.NET战略, 2000年6月30日. 微软公司于2002年2月13日正式推出第一代.N ...

  2. Django(24)永久重定向和临时重定向

    重定向 重定向分为永久重定向和临时重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面.比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面. ...

  3. [刷题] 435 Non-overlapping Intervals

    要求 贪心算法与动态规划的关系 给定一组区间,最少删除多少个区间,可以让这些区间之间互相不重叠 给定区间的起始点永远小于终止点 示例 [[1,2],[2,3],[3,4],[1,3]], 返回1 [[ ...

  4. [Java] javaEE

    定义 面向企业级应用中一些通用模块制定的标准 避免重复开发,解决代码可靠性问题 13种规范 JDBC(JavaDatabase Connectivity):数据库连接 以统一方式访问数据库的API J ...

  5. 寻找CPU使用率高的进程方法

    寻找CPU使用率高的进程方法 发布时间:  2017-07-13 浏览次数:  1362 下载次数:  0 问题描述 节点报CPU使用率高,甚至出现"ALM-12016 CPU使用率超过阈值 ...

  6. 3.1.5 LTP(Linux Test Project)学习(五)-LTP代码学习

    3.1.5 LTP(Linux Test Project)学习(五)-LTP代码学习 Hello小崔 ​ 华为技术有限公司 Linux内核开发 2 人赞同了该文章 LTP代码学习方法主要介绍两个步骤, ...

  7. shell 获取MySQL查询结果并处理

    主要应用到shell for循环 定义数据库连接信息 HOST_NAME='127.0.0.1' DB_PORT='3306' DB_NAME='数据库名' USER_NAME='root' PASS ...

  8. Prometheus存储原理及数据备份还原

    prometheus将采集到的样本以时间序列的方式保存在内存(TSDB 时序数据库)中,并定时保存到硬盘中.与zabbix不同,zabbix会保存所有的数据,而prometheus本地存储会保存15天 ...

  9. https 真的安全吗,可以抓包吗,如何防止抓包吗

    Android_interview github 地址 大家好,我是程序员徐公,加上实习,有五年中大厂经验.自荐一下,可以关注我的微信公众号程序员徐公 公众号程序员徐公回复黑马,获取 Android ...

  10. SpringMVC学习笔记-REST风格请求实现

    RESTful概念及功能 RESTful的概念:RESTful是 一种资源定位及资源操作的风格,其本身既不是标准也不是协议,而是一种设计风格,可以使得软件整体层次更加分明.代码更加简洁,并且有利于实现 ...