怎么说呢这一场考得还算可以呢

拿了120pts,主要是最后一个题灵光开窍,想起来是tarjan,然后勉勉强强拿了40pts,本来是可以拿满分的,害

没事考完了就要反思

这场考试我心态超好,从第一个题开始打暴力,一直打到第三题,嘿嘿自我感觉良好

不过我这个dfs的能力还是差了一点,得在磨练磨练!!!

要保持这种状态先打暴力,再想正解!!!加油!!!

那就是正解环节了。。。。

T1  string

第一眼看到这个题,我说:::暴力有分了,看我10min把它A了,来来来。。。

我就上了

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=100005;
int n,m;
char ch[N];
signed main(){
scanf("%d%d",&n,&m);
scanf("%s",ch+1);
for(re i=1;i<=m;i++){
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
if(x==1)sort(ch+l,ch+r+1);
else sort(ch+l,ch+r+1,greater<char>());
}
for(re i=1;i<=n;i++)printf("%c",ch[i]);
}

sort大法好,直接拿到四十分,

于是就这样我们成功的打完了第一题,用时:7min

不扯了,上正解

我们发现,上面的代码复杂度是m*n*logn的

所以我们尽量省去一个n,m是肯定在这里的,搞不掉

大佬们说过一句话:

遇到线性问题时,我们要用线段树;  

遇到树上的问题时,我们要用dfs序+线段树;

那这个题我们就尝试用线段树来解决问题;

一开始是这么想的,每个点就存这个点的字符就好了,后来发现我连线段树是啥都不知道了,竟然只维护点。。。

在每个点上加一个桶(这名词高大上吧!!就是开个数组,统计每个字符出现的次数)

然后就可以开开心心的用了

每一次排序,先把这个区间内的所有权值都提取出来,再一部分一部分的插入回去

一开始我觉得这样会T的吧,但是好像没有比这个更快的办法了

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define mem(a) memset(a,0,sizeof(a))
const int N=100005;
int n,m;
char ch[N];
int num[N];
int now[27];
struct node{
#define ls x<<1
#define rs x<<1|1
int val[N*4][27];
int laz[N*4];
inline void pushup(int x){
for(re i=1;i<=26;i++)
val[x][i]=val[ls][i]+val[rs][i];
}
inline void pushdown(int x,int l,int r){
if(laz[x]){
int mid=l+r>>1;
laz[ls]=laz[x];
laz[rs]=laz[x];
mem(val[ls]);
mem(val[rs]);
val[ls][laz[x]]=mid-l+1;
val[rs][laz[x]]=r-mid;
laz[x]=0;
}
}
inline void build(int x,int l,int r){
if(l==r){
val[x][num[l]]=1;
//cout<<(char)(num[l]+'a'-1)<<" ";
return ;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(x);
}
inline void ins(int x,int l,int r,int ql,int qr,int c){
//if(ql>qr)return ;
if(ql<=l&&r<=qr){
laz[x]=c;
mem(val[x]);
val[x][c]=r-l+1;
return ;
}
pushdown(x,l,r);
int mid=l+r>>1;
if(ql<=mid)ins(ls,l,mid,ql,qr,c);
if(qr>mid)ins(rs,mid+1,r,ql,qr,c);
pushup(x);
}
inline void query(int x,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){
for(re i=1;i<=26;i++)now[i]+=val[x][i];
return ;
}
pushdown(x,l,r);
int mid=l+r>>1;
if(ql<=mid)query(ls,l,mid,ql,qr);
if(qr>mid)query(rs,mid+1,r,ql,qr);
}
inline void dfs(int x,int l,int r){
if(l==r){
for(re i=1;i<=26;i++)
if(val[x][i]){
printf("%c",i+'a'-1);
break;
}
return ;
}
pushdown(x,l,r);
int mid=l+r>>1;
dfs(ls,l,mid);
dfs(rs,mid+1,r);
}
#undef ls
#undef rs
}xds;
signed main(){
scanf("%d%d",&n,&m);
scanf("%s",ch+1);
for(re i=1;i<=n;i++)num[i]=ch[i]-'a'+1;
xds.build(1,1,n);
for(re i=1;i<=m;i++){
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
mem(now);
xds.query(1,1,n,l,r);
if(x==1){
for(re i=1;i<=26;i++){
if(!now[i])continue;
xds.ins(1,1,n,l,l+now[i]-1,i);
l+=now[i];
}
}
else{
for(re i=26;i>=1;i--){
if(!now[i])continue;
xds.ins(1,1,n,l,l+now[i]-1,i);
l+=now[i];
}
}
}
xds.dfs(1,1,n);
}

T1

过了这个题我觉得我又行了哈哈哈

T2   matrix

这个我在考场上的时候想了一个多小时,可就是没想出来,一直在捣鼓我的组合数,就没往dp上想

然后喜提0蛋一个;;;;;;

虽然我觉得我的思路还是没有问题滴但他就是错了

正解是dp诶

我们从左往右转移,那么我们发现左区间的方案数是可以直接通过A求得的

所以我们把重点放在右区间的转移上,

我们设dp[i][j]表示,目前到了第i列,右区间内放了j个1,左区间的方案在每一次转移的时候乘上就好了

那么我们就先考虑左区间的转移:

(等会我们先处理一个数据,这里l[i]表示左区间的终点(就是输入的l)小于等于i的个数,r[i]表示右区间的起点(就是输入的r)小于等于i的个数)

  1、l[i]==l[i-1]那么这个时候没有新的1要放进去,所以直接由上一个状态转移  dp[i][j]+=dp[i-1][j];

  2、l[i]>l[i-1]这个时候乘方案数了,此时一共放了j+l[i-1]个1,所以还剩下i-j-l[i-1]列可以放1,要放进去l[i]-l[i-1]个点所以乘上A(i-j-l[i-1],l[i]-l[i-1])

哎呀呀,上面那个太麻烦了,直接乘不就好了嘛,反正l[i]==l[i-1]的时候A返回的是1,,,都一样都一样啦

再考虑右区间的转移:就按照上面说的,不用分开算了

右区间一定一定从dp[i-1][j-1]转移过来因为只能加一个1嘛,这没问题吧,再乘上目前有多少个点可以用来放1,乘上A

所以dp[i][j]+=dp[i-1][j-1]*(r[i]-(j-1))*A(A同上)

因为最后一定会占满所有的矩阵,所以答案就是dp[m][n];

代码来了

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=3005;
const int mod=998244353;
int n,m;
int f[N],b[N];
int l[N],r[N];
int jc[N],inv[N];
int dp[N][N],ans;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ret;
}
int A(int x,int y){
//cout<<jc[x]<<" "<<inv[x-y]<<endl;
return 1ll*jc[x]*inv[x-y]%mod;
}
signed main(){
scanf("%d%d",&n,&m);
for(re i=1;i<=n;i++){
scanf("%d%d",&f[i],&b[i]);
l[f[i]]++;
r[b[i]]++;
}
dp[0][0]=1;jc[0]=1;
for(re i=1;i<=m;i++){
l[i]+=l[i-1];
r[i]+=r[i-1];
jc[i]=1ll*jc[i-1]*i%mod;
}
inv[0]=1;inv[m]=ksm(jc[m],mod-2);
//cout<<inv[m]<<endl;
for(re i=m-1;i>=1;i--){
inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
for(re i=1;i<=m;i++){
for(re j=0;j<=r[i];j++){
//cout<<dp[i][j]<<endl;
if(l[i]==l[i-1])dp[i][j]=(1ll*dp[i][j]+dp[i-1][j])%mod;
else dp[i][j]=1ll*dp[i-1][j]*A(i-j-l[i-1],l[i]-l[i-1])%mod;
if(j) dp[i][j]=(dp[i][j]+1ll*dp[i-1][j-1]*(r[i]-(j-1))%mod*A(i-j-l[i-1],l[i]-l[i-1])%mod)%mod;
}
}
printf("%d",dp[m][n]);
}

T2

(心情愉悦,粘张图片)

所以T3 big

那么这个题我暴力拿到了40pts,下面是暴力代码,分别求取前缀和,然后枚举

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+10;
int n,m;
int a[N],fro[N];
int maxn,ans,sum;
signed main(){
scanf("%d%d",&n,&m);
for(re i=1;i<=m;i++){
scanf("%d",&a[i]);
fro[i]=fro[i-1]^a[i];
}
for(re i=0;i<(1<<n);i++){
int tmp;maxn=(1<<n);
for(re j=0;j<=m;j++){
tmp=i;
tmp^=fro[j];
tmp=(2*tmp/(1<<n)+2*tmp)%(1<<n);
tmp^=fro[m]^fro[j];
if(tmp<maxn)maxn=tmp;
}
if(maxn==ans)sum++;
if(maxn>ans)ans=maxn,sum=1;
}
printf("%d\n%d",ans,sum);
}

全部都是TLE啊

然后正解其实是trie树

你发现他给你的那一长串,就是把x循环左移

就是1100000变成1000001,就是左移一位,然后最高位的放到最低位去,循环节就是n

然后左移的操作就成为一个分界点

可以发现如果将一个数左移一下,就相当于把他自己和那些要异或的数都左移以为

然后我们枚举每个分界点,的到了一个数组存的就是可以通过一步异或得到答案的数组

把它们放到trie树上

然后如果有一位既能取到0,也能取到1,那这一位就是一个无效位,不会对答案作出任何贡献,因为无论你拿出来的数是啥我总能把这一位变成0

所以这样就可以统计最大值和数目了

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=100005;
int n,m;
int a[N],fro[N],beh[N];
int b[N];
struct trie{
int v,son[2];
}tr[N*50];
int seg;
int an;
long long su;
void ins(int x){
int u=0;
for(re i=n-1;i>=0;i--){
int tmp=1&(x>>i);
//cout<<tmp<<" ";
if(!tr[u].son[tmp])
tr[u].son[tmp]=++seg;
u=tr[u].son[tmp];
}
//cout<<endl;
}
void dfs(int x,int dep,int ans,long long sum){
dep--;
if(dep==-1){
if(an<ans)an=ans,su=1;
else if(an==ans)su++;
}
//cout<<x<<" "<<dep<<" "<<tr[x].son[0]<<" "<<tr[x].son[1]<<endl;
if(!tr[x].son[0]&&!tr[x].son[1])return ;
if(!tr[x].son[0]||!tr[x].son[1]){
ans+=(1<<dep);
if(tr[x].son[0])dfs(tr[x].son[0],dep,ans,sum);
else dfs(tr[x].son[1],dep,ans,sum);
return ;
}
dfs(tr[x].son[0],dep,ans,sum);
dfs(tr[x].son[1],dep,ans,sum);
}
signed main(){
scanf("%d%d",&n,&m);
for(re i=1;i<=m;i++){
scanf("%d",&a[i]);
fro[i]=fro[i-1]^a[i];
}
for(re i=1;i<=m;i++){
int t=a[i]>>(n-1);
a[i]=a[i]<<1;
a[i]|=t;
b[i]=b[i-1]^a[i];
}
for(re i=0;i<=m;i++){
b[i]^=fro[m]^fro[i];
//cout<<b[i]<<endl;
ins(b[i]);
}
dfs(0,n,0,1);
printf("%d\n%lld",an,su);
}

T3

这么看的话,这个题也不算难

T4   所驼门王的宝藏

说实话这题真水,水暴了

就一个tarjan缩点

+临接表+map+记忆化搜索

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define pa pair<int,int>
const int N=2000100;
int n,r,c;
struct node{
int x,y,typ;
}mea[N];
struct edge{
int nxt,id;
}xe[N*2],ye[N*2];
int hrp,xea[N],yea[N];
map<pa,int> zym;
int zx[10]={0,-1,-1,-1,0,0,1,1,1};
int zy[10]={0,-1,0,1,-1,1,-1,0,1};
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
int dfn[N],low[N],cnt;
int pos[N],val[N],col;
int du[N],dp[N];
stack<int> q;
void dfs(int x){
dfn[x]=low[x]=++cnt;
q.push(x);
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(!dfn[y]){
dfs(y);
low[x]=min(low[x],low[y]);
}
else if(!pos[y]){
low[x]=min(low[x],low[y]);
}
}
if(dfn[x]==low[x]){
pos[x]=++col;
val[col]++;
while(x!=q.top()&&!q.empty()){
int y=q.top();//cout<<y<<" ";
q.pop();
pos[y]=col;
val[col]++;
}
q.pop();
}
}
int t_[N*2],n_[N*2],h_[N*2],r_;
void add_ed(int x,int y){
t_[++r_]=y;
n_[r_]=h_[x];
h_[x]=r_;
}
int vis[N],ans;
void dfs_(int x){
if(dp[x]>val[x])return ;
dp[x]=val[x];
for(re i=h_[x];i;i=n_[i]){
int y=t_[i];
dfs_(y);
dp[x]=max(dp[x],dp[y]+val[x]);
}
}
signed main(){
scanf("%d%d%d",&n,&r,&c);
for(re i=1;i<=n;i++){
scanf("%d%d%d",&mea[i].x,&mea[i].y,&mea[i].typ);
xe[++hrp].id=i;xe[hrp].nxt=xea[mea[i].x];xea[mea[i].x]=hrp;
ye[hrp].id=i;ye[hrp].nxt=yea[mea[i].y];yea[mea[i].y]=hrp;
pa a=(pa){mea[i].x,mea[i].y};zym[a]=i;
}
for(re i=1;i<=n;i++){
int x=mea[i].x,y=mea[i].y,typ=mea[i].typ;
if(typ==1){
for(re j=xea[x];j;j=xe[j].nxt)
if(i!=xe[j].id)add_edg(i,xe[j].id);
}
else if(typ==2){
for(re j=yea[y];j;j=ye[j].nxt)
if(i!=ye[j].id)add_edg(i,ye[j].id);
}
else{
for(re j=1;j<=8;j++){
pa a=(pa){x+zx[j],y+zy[j]};
if(zym[a])add_edg(i,zym[a]);
}
}
}
for(re i=1;i<=n;i++)
if(!dfn[i])dfs(i);
for(re i=1;i<=n;i++){
for(re j=head[i];j;j=nxt[j])
if(pos[i]!=pos[to[j]])
add_ed(pos[i],pos[to[j]]),du[pos[to[j]]]++;//cout<<pos[to[j]]<<" ";
}
for(re i=col;i>=1;i--){
if(du[i]==0){
dfs_(i);
ans=max(ans,dp[i]);
}
}
printf("%d",ans);
}

T3

完结撒花!!!!!

noip模拟5[string·matrix·big·所驼门王的宝藏]的更多相关文章

  1. 8.18 NOIP模拟测试25(B) 字符串+乌鸦喝水+所驼门王的宝藏

    T1 字符串 卡特兰数 设1为向(1,1)走,0为向(1,-1)走,限制就是不能超过$y=0$这条线,题意转化为从(0,0)出发,走到(n+m,n-m)且不越过$y=0$,然后就裸的卡特兰数,$ans ...

  2. 【BZOJ-1924】所驼门王的宝藏 Tarjan缩点(+拓扑排序) + 拓扑图DP

    1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 787  Solved: 318[Submit][Stat ...

  3. [BZOJ 1924][Sdoi2010]所驼门王的宝藏

    1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1285  Solved: 574[Submit][Sta ...

  4. 「BZOJ1924」「SDOI2010」 所驼门王的宝藏 tarjan + dp(DAG 最长路)

    「BZOJ1924」[SDOI2010] 所驼门王的宝藏 tarjan + dp(DAG 最长路) -------------------------------------------------- ...

  5. 【题解】SDOI2010所驼门王的宝藏(强连通分量+优化建图)

    [题解]SDOI2010所驼门王的宝藏(强连通分量+优化建图) 最开始我想写线段树优化建图的说,数据结构学傻了233 虽然矩阵很大,但是没什么用,真正有用的是那些关键点 考虑关键点的类型: 横走型 竖 ...

  6. BZOJ 1924: [Sdoi2010]所驼门王的宝藏 【tarjan】

    Description 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为“先 知”的Alpaca L. Sotomon 是这个家族的领袖,外人也称其为“所驼门王”.所 驼门王毕生致力于维 ...

  7. [SDOI2010]所驼门王的宝藏

    题目描述 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为"先知"的Alpaca L. Sotomon是这个家族的领袖,外人也称其为"所驼门王". ...

  8. 【BZOJ1924】【SDOI2010】所驼门王的宝藏(Tarjan,SPFA)

    题目描述 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为"先知"的Alpaca L. Sotomon是这个家族的领袖,外人也称其为"所驼门王". ...

  9. [LuoguP2403][SDOI2010]所驼门王的宝藏

    题目描述 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为"先知"的Alpaca L. Sotomon是这个家族的领袖,外人也称其为"所驼门王". ...

随机推荐

  1. 病毒木马查杀实战第024篇:MBR病毒之编程解析引导区

    前言 通过之前的学习,相信大家已经对磁盘的引导区有了充分的认识.但是我们之前的学习都是利用现成的工具来对引导区进行解析的,而对于一名反病毒工程师而言,不单单需要有扎实的逆向分析功底,同时也需要有很强的 ...

  2. 010 Editor体验

    源代码的我们现在拥有各式各样的IDE和编辑器可以去查看,但二进制文件对于大多数软件只能做到显示16进制,而不能按照文件类型的格式去显示.今天我们就用dex文件让010 show. 安装软件: http ...

  3. coding push 上传文件

    git config --global user.name "lyshark" &git config --global user.email "11815068 ...

  4. Windows核心编程 第四章 进程(上)

    第4章 进 程     本章介绍系统如何管理所有正在运行的应用程序.首先讲述什么是进程,以及系统如何创建进程内核对象,以便管理每个进程.然后将说明如何使用相关的内核对象来对进程进行操作.接着,要介绍进 ...

  5. 神经网络与机器学习 笔记—LMS(最小均方算法)和学习率退火

    神经网络与机器学习 笔记-LMS(最小均方算法)和学习率退火 LMS算法和Rosenblatt感知器算法非常想,唯独就是去掉了神经元的压制函数,Rosenblatt用的Sgn压制函数,LMS不需要压制 ...

  6. [C#] 使用 NAudio 实现音频可视化

    预览: 捕捉声卡输出: 实现音频可视化, 第一步就是获得音频采样, 这里我们选择使用计算机正在播放的音频作为采样源进行处理: NAudio 中, 可以借助 WasapiLoopbackCapture ...

  7. 将一个eclipse的SSM项目用IDEA打开并运行

    项目部署 将一个eclipse项目用idea打开,并且 部署到tomcat中 .或者你tomcat部署成功,但是启动就是404,下面的步骤就要更认真看了 项目配置 打开idea,Import Proj ...

  8. 集成Spring Data JPA

    1.Spring Data JPA简介 Spring Data是一个用于简化数据访问,并支持云服务的开源框 使用完成Spring Data JPA对user表的CRUD操作. 2.步骤 1.创建工程勾 ...

  9. MindSpore模型验证

    技术背景 在前面一篇博客中,我们介绍了MindSpore在机器学习过程中保存和加载模型的方法.这种将模型存储为静态文件的做法,使得我们可以更灵活的使用训练出来的模型,比如用于各种数据集的验证,或者是迁 ...

  10. 在Visual Studio 中使用git——文件管理-下(六)

    在Visual Studio 中使用git--什么是Git(一) 在Visual Studio 中使用git--给Visual Studio安装 git插件(二) 在Visual Studio 中使用 ...