两个有趣的题目
题目描述
现在有一个长度为
问可以得到的最小字典序序列是什么?
思路点拨
不论你如何操作,如果两个位置
-
对于
,如果 我们就让 向 连有向边。 -
所以一个答案对应了有向图上的一个拓扑序,我们希望这个拓扑序字典序尽可能的小直接将拓扑排序的队列换成堆即可。
时间复杂度上界
考虑进行优化,我们连边的方式的关系比较特殊,对于节点
拓扑排序的过程中我们每取出一个节点就将满足
我们发现树套树没有必要维护
代码实现时离散化会更加方便。
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
using namespace std;
namespace IO{
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void print(int x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
}
using namespace IO;
const int MAXN=1e5+10,inf=1e9;
int n,k;
int a[MAXN],deg[MAXN],tmp[MAXN];
map<int,int> vis;
int bit[MAXN];
void add(int x){
for(int i=x;i<=n;i+=lowbit(i)) bit[i]++;
}
int query(int l,int r){
int cnt=0;
for(int i=r;i;i-=lowbit(i)) cnt+=bit[i];
for(int i=l-1;i;i-=lowbit(i)) cnt-=bit[i];
return cnt;
}
struct node{
int x,y;
node(int x_=0,int y_=0){x=x_,y=y_;}
}t[MAXN<<2];
int tag[MAXN<<2];
void pushup(int i){
if(t[i<<1].y==t[i<<1|1].y)
t[i]=node(min(t[i<<1].x,t[i<<1|1].x),t[i<<1].y);
else t[i]=(t[i<<1].y<t[i<<1|1].y)?t[i<<1]:t[i<<1|1];
}
void pushdown(int i){
t[i<<1].y+=tag[i],t[i<<1|1].y+=tag[i];
tag[i<<1]+=tag[i],tag[i<<1|1]+=tag[i];
tag[i]=0;
}
void build(int i,int l,int r){
if(l==r){
t[i]=node(l,deg[l]);
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid),build(i<<1|1,mid+1,r);
pushup(i);
}
void update(int i,int l,int r,int L,int R,int w){
if(l>R||r<L) return ;
if(L<=l&&r<=R){
t[i].y+=w,tag[i]+=w;
return ;
}
int mid=(l+r)>>1;
pushdown(i);
update(i<<1,l,mid,L,R,w);
update(i<<1|1,mid+1,r,L,R,w);
pushup(i);
}
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++)
a[i]=tmp[i]=read();
sort(tmp+1,tmp+n+1);
for(int i=1;i<=n;i++){
vis[a[i]]++;
a[i]=lower_bound(tmp+1,tmp+n+1,a[i])-tmp-1+vis[a[i]];
}
for(int i=1;i<=n;i++){
int w=tmp[a[i]];
int L=lower_bound(tmp+1,tmp+n+1,w-k)-tmp,R=upper_bound(tmp+1,tmp+n+1,w+k)-tmp-1;
deg[a[i]]=(i-1)-query(L,R);
add(a[i]);
}
build(1,1,n);
for(int i=1;i<=n;i++){
int w=t[1].x;
print(tmp[w]),putchar('\n');
int L=lower_bound(tmp+1,tmp+n+1,tmp[w]-k)-tmp,R=upper_bound(tmp+1,tmp+n+1,tmp[w]+k)-tmp-1;
if(L>1) update(1,1,n,1,L-1,-1);
if(R<n) update(1,1,n,R+1,n,-1);
update(1,1,n,w,w,inf);
}
return 0;
}
题目描述
现在有一个长度为
问可以通过操作得到多少本质不同的序列?
思路点拨
我们同样按照上述的想法去连边,例如:如果
然后答案对应了这个有向图上的一个拓扑序。但是拓扑序计数是npc问题,所以这个图一定是比较特殊的。
题目讲到
考虑进行动态规划,定义
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void print(int x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
}
using namespace IO;
const int MAXN=5e3+10,mod=1e9+7;
int T,n,a[MAXN];
int f[MAXN][MAXN],pos[MAXN][2],pre[MAXN];
signed main(){
T=read();
while(T--){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
int cnt[2]={0,0};
for(int i=1;i<=n;i++){
pos[++cnt[a[i]&1]][a[i]&1]=i;
pre[i]=0;
for(int j=i-1;j;j--)
if((a[i]&1)!=(a[j]&1)&&abs(a[i]-a[j])!=1){
pre[i]=j;
break;
}
}
for(int i=0;i<=cnt[0];i++)
for(int j=0;j<=cnt[1];j++) f[i][j]=0;
f[0][0]=1;
for(int i=0;i<=cnt[0];i++)
for(int j=0;j<=cnt[1];j++){
if(i<cnt[0]&&pre[pos[i+1][0]]<=pos[j][1])
f[i+1][j]=(f[i+1][j]+f[i][j])%mod;
if(j<cnt[1]&&pre[pos[j+1][1]]<=pos[i][0])
f[i][j+1]=(f[i][j+1]+f[i][j])%mod;
}
print(f[cnt[0]][cnt[1]]),putchar('\n');
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现