「补档」近期咕掉的一些题解
患有严重强迫症的博主只有把一场考试三道题都改完才会产生写题解的念头(明明是因为太懒了好吧)
导致有很多场T1T2都没有写
然而今天他良心发现了QAQ
61A.砖块
美妙的小模拟
题面提示我们砖块的位置可以由它左下角格子的坐标表示,
那么再用一个变量表示它的站立情况就能得到全部信息了。大力分类讨论即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#define pa pair<int,int>
using namespace std;//多测清空!!!
int T,n,stan,nowx,nowy,ans;
char s[1005];
//0 直立
//1 平躺
//2 侧躺
map<pa,int> vis;
void add()
{
if(stan==0)
{
vis[make_pair(nowx,nowy)]++;
ans=max(ans,vis[make_pair(nowx,nowy)]);
}
else if(stan==1)
{
for(int i=nowy;i<=nowy+n-1;i++)
vis[make_pair(nowx,i)]++,ans=max(ans,vis[make_pair(nowx,i)]);
}
else if(stan==2)
{
for(int i=nowx;i<=nowx+n-1;i++)
vis[make_pair(i,nowy)]++,ans=max(ans,vis[make_pair(i,nowy)]);
}
}
void work()
{
scanf("%d",&n);
scanf("%s",s+1);
vis.clear();
int len=strlen(s+1);
stan=0,nowx=0,nowy=0,ans=0;
add();
for(int i=1;i<=len;i++)
{
if(s[i]=='N')
{
if(stan==0)
{
stan=1;nowy++;
}
else if(stan==1)
{
stan=0;nowy+=n;
}
else if(stan==2)
{
nowy++;
}
}
if(s[i]=='S')
{
if(stan==0)
{
stan=1;nowy-=n;
}
else if(stan==1)
{
stan=0;nowy--;
}
else if(stan==2)
{
nowy--;
}
}
if(s[i]=='W')
{
if(stan==0)
{
stan=2;
nowx-=n;
}
else if(stan==1)
{
nowx--;
}
else if(stan==2)
{
stan=0;
nowx--;
}
}
if(s[i]=='E')
{
if(stan==0)
{
stan=2;
nowx++;
}
else if(stan==1)
{
nowx++;
}
else if(stan==2)
{
stan=0;
nowx+=n;
}
}
add();
}
if(stan==0)
{
//puts("Stand");
cout<<nowx<<endl<<nowy<<endl;
printf("%d\n",ans);
}
else if(stan==1)
{
//puts("shu");
for(int i=nowy;i<=nowy+n-1;i++)
printf("%d ",nowx);
putchar('\n');
for(int i=nowy;i<=nowy+n-1;i++)
printf("%d ",i);
putchar('\n');
printf("%d\n",ans);
}
else if(stan==2)
{
//puts("ce");
for(int i=nowx;i<=nowx+n-1;i++)
printf("%d ",i);
putchar('\n');
for(int i=nowx;i<=nowx+n-1;i++)
printf("%d ",nowy);
putchar('\n');
printf("%d\n",ans);
}
}
int main()
{
//freopen("bl.out","w",stdout);
scanf("%d",&T);
while(T--)work();
return 0;
}
61C.甜圈
算是个线段树维护懒标记的板子吧,用来练手挺不错的。两种解法。
考场上写的第一种:
每个线段树结点维护三个值:$a[]$(这段区间的覆盖情况,相同且合法就为顶层的任务,否则为-1)、$lz1[]$(下层懒标记)、$lz2[]$(上层懒标记)。
对于懒标记,有当前任务值、-1(已废掉)、-2(空)三种状态。$pushdown$时分类讨论,尝试将父亲的下层和儿子的上层合并即可。
第二种:
把任务叠加情况Hash,用线段树维护。转化成区间加区间乘的板子(洛谷线段树2)。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=2e5+5;
int n,K,m,ans=0;
int a[N<<2],lz1[N<<2],lz2[N<<2];
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
void build(int k,int l,int r)
{
a[k]=0;lz1[k]=lz2[k]=-2;
if(l==r)return ;
int mid=l+r>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
}
void down(int k)
{
if(lz1[k]==-2)return ;
if(lz1[k]==-1)
{
a[ls(k)]=a[rs(k)]=lz1[ls(k)]=lz2[ls(k)]=lz1[rs(k)]=lz2[rs(k)]=-1;
return ;
}
if(a[ls(k)]!=-1)
{
if(a[ls(k)]+1!=lz1[k])a[ls(k)]=-1;
else a[ls(k)]=lz2[k];
}
if(a[rs(k)]!=-1)
{
if(a[rs(k)]+1!=lz1[k])a[rs(k)]=-1;
else a[rs(k)]=lz2[k];
}
if(lz1[ls(k)]!=-1)
{
if(lz1[ls(k)]==-2)lz1[ls(k)]=lz1[k],lz2[ls(k)]=lz2[k];
else if(lz2[ls(k)]+1!=lz1[k])lz1[ls(k)]=lz2[ls(k)]=-1;
else lz2[ls(k)]=lz2[k];
}
if(lz1[rs(k)]!=-1)
{
if(lz1[rs(k)]==-2)lz1[rs(k)]=lz1[k],lz2[rs(k)]=lz2[k];
else if(lz2[rs(k)]+1!=lz1[k])lz1[rs(k)]=lz2[rs(k)]=-1;
else lz2[rs(k)]=lz2[k];
}
lz1[k]=lz2[k]=-2;
}
void change(int k,int l,int r,int L,int R,int val)
{
if(L<=l&&R>=r)
{
if(a[k]!=-1)
{
if(a[k]+1!=val)a[k]=-1;
else a[k]=val;
}
if(lz1[k]!=-1)
{
if(lz1[k]==-2)lz1[k]=lz2[k]=val;
else if(lz2[k]+1!=val)lz1[k]=lz2[k]=-1;
else lz2[k]=val;
}
return ;
}
down(k);
int mid=l+r>>1;
if(L<=mid)change(ls(k),l,mid,L,R,val);
if(R>mid)change(rs(k),mid+1,r,L,R,val);
}
void dfs(int k,int l,int r)
{
if(l==r)
{
//cout<<l<<' '<<a[k]<<endl;
if(a[k]==K)ans++;
return ;
}
down(k);
int mid=l+r>>1;
dfs(ls(k),l,mid);
dfs(rs(k),mid+1,r);
}
int main()
{
/*freopen("dt.in","r",stdin);
freopen("my.out","w",stdout);*/
n=read();K=read();m=read();
build(1,1,n);
while(m--)
{
int l=read(),r=read(),val=read();
/*puts(" ");
show(1,1,n);*/
change(1,1,n,l,r,val);
}
dfs(1,1,n);
cout<<ans<<endl;
return 0;
}
59A.Reverse
对于每一个位置,向能翻转到的位置连边,然后跑bfs。
但这样时空双炸,所以用set维护当前可以选的位置集合。对于奇偶位置分别用一个set维护。
细节:
node[now].erase(it++);
不能写成:
node[now].erase(it);it++;
80b A.最长不下降子序列
显然有循环节。先跑循环节长度个循环节避免特殊情况,之后每段循环接的贡献都只能为1,最后处理一下结尾剩余段就好了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1e6+50;
typedef long long ll;
int a[N*3],A,B,C,D,vis[N],dp[N*3],b[N*3];
ll lim,len,beg,n;
vector<int> s;
void show()
{
puts("QAQ");
for(int i=1;i<=n;i++)
cout<<a[i]<<' ';
puts(" ");
}
struct BIT
{
int c[N*3];
void ini()
{
memset(c,0,sizeof(c));
}
int lb(int x){return x&-x;}
void add(int x,int val)
{
for( ;x<=150;x+=lb(x))
c[x]=max(val,c[x]);
}
int ask(int x)
{
int res=0;
for( ;x;x-=lb(x))
res=max(res,c[x]);
return res;
}
}bit;
void qj()
{
for(int i=2;i<=lim;i++)
a[i]=1LL*(1LL*a[i-1]*a[i-1]*A+1LL*B*a[i-1]+C)%D;
ll res=0;
for(int i=1;i<=lim;i++)
{
dp[a[i]]=max(dp[a[i]],bit.ask(a[i]+1)+1);
bit.add(a[i]+1,dp[a[i]]);
res=max(res,1LL*dp[a[i]]);
}
printf("%d\n",res);
}
int main()
{
scanf("%lld",&lim);
scanf("%d%d%d%d%d",&a[1],&A,&B,&C,&D);
if(lim<=N-50)
{
qj();return 0;
}
n=1;
for(int i=2;i<=lim;i++)
{
n++;
a[i]=1LL*(1LL*a[i-1]*a[i-1]*A+1LL*B*a[i-1]+C)%D;
if(vis[a[i]])
{
n--;
len=i-vis[a[i]];beg=vis[a[i]];
break;
}
else vis[a[i]]=i;
}
//show();
if(n+40*len>=lim)
{
qj();return 0;
}
for(int c=1;c<=40;c++)
for(int i=1;i<=len;i++)
n++,a[n]=a[n-len];
ll res=0;
for(int i=1;i<=n;i++)
{
dp[a[i]]=max(dp[a[i]],bit.ask(a[i]+1)+1);
bit.add(a[i]+1,dp[a[i]]);
res=max(res,1LL*dp[a[i]]);
}
ll num=(lim-n)/len;
ll rest=(lim-n)%len;
for(int i=beg;i<=beg+rest-1;i++)
{
if(dp[a[i]]==res)res++;
}
printf("%lld\n",res+num);
return 0;
}
80b C.最近公共祖先
考虑直接维护答案。假设现在改了节点$x$的颜色,那么显然$x$的子树的答案都可以更新了。另外,$x$的祖先$f$除$x$所在链之外的子树中节点的答案也可以被更新。线段树维护dfs序即可,每次跳父亲直到遇到黑点。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+5
;
int n,m,w[N];
int head[N],nxt[N<<1],tot,to[N<<1];
int dfn[N],size[N],ind,c[N<<2],lz[N<<2],col[N],fa[N];
inline void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int f)
{
dfn[x]=++ind;size[x]=1;
fa[x]=f;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
dfs(y,x);
size[x]+=size[y];
}
}
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define up c[k]=max(c[ls(k)],c[rs(k)])
inline void down(int k)
{
if(lz[k]==-1)return ;
lz[ls(k)]=max(lz[k],lz[ls(k)]);
lz[rs(k)]=max(lz[rs(k)],lz[k]);
c[ls(k)]=max(c[ls(k)],lz[k]);
c[rs(k)]=max(c[rs(k)],lz[k]);
lz[k]=-1;
}
void change(int k,int l,int r,int L,int R,int val)
{
if(L>R)return ;
if(L<=l&&R>=r)
{
c[k]=max(c[k],val);
lz[k]=max(lz[k],val);
return ;
}
int mid=l+r>>1;
down(k);
if(L<=mid)change(ls(k),l,mid,L,R,val);
if(R>mid)change(rs(k),mid+1,r,L,R,val);
up;
}
int query(int k,int l,int r,int pos)
{
if(l==r)return c[k];
int mid=l+r>>1;
down(k);
if(pos<=mid)return query(ls(k),l,mid,pos);
else return query(rs(k),mid+1,r,pos);
}
inline void modify(int s,int t,int num)
{
if(t==0)change(1,1,n,dfn[s],dfn[s]+size[s]-1,num);
else
{
change(1,1,n,dfn[s],dfn[t]-1,num);
change(1,1,n,dfn[t]+size[t],dfn[s]+size[s]-1,num);
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
w[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
dfs(1,0);
char str[11];
memset(lz,-1,sizeof(lz));
memset(c,-1,sizeof(c));
for(int i=1;i<=m;i++)
{
scanf("%s",str+1);int u=read();
if(str[1]=='M')
{
int last=0;
for(;u;last=u,u=fa[u])
{
//cout<<"QAQ "<<u<<endl;
modify(u,last,w[u]);
//cout<<"QAQ "<<query(1,1,n,dfn[u])<<endl;
if(col[u])break;
col[u]=1;
}
}
else
printf("%d\n",query(1,1,n,dfn[u]));
}
}
77A.位运算
大力按位分类讨论即可,写的稍麻烦不过很无脑。
注意取位的方式。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
typedef long long ll;
int T,ra,ro,rx;
int bit[35];//1: all 1 2:all 0 3:one 1 one 0 4:2 or 3 5:1 or 3 6:1 or 2
const ll num[8]={4,1,1,2,3,3,2};
void work()
{
for(int i=0;i<=32;i++)
bit[i]=0;
ra=read();ro=read();rx=read();
if((ra==-1&&ro==-1)||(ro==-1&&rx==-1))
{
puts("inf");return ;
}
if(ra!=-1)
{
for(int i=0;i<=30;i++)
{
int now=(ra>>i)&1;
if(now==0)bit[i]=4;
else bit[i]=1;
}
}
if(ro!=-1)
{
for(int i=0;i<=30;i++)
{
int now=(ro>>i)&1;
if(!bit[i])
{
if(now==0)bit[i]=2;
else bit[i]=5;
}
else
{
if(now==0&&bit[i]==1)
{
puts("0");return ;
}
else if(now==0&&bit[i]==4)
bit[i]=2;
else if(now==1&&bit[i]==1)
continue;
else if(now==1&&bit[i]==4)
bit[i]=3;
}
}
}
if(rx!=-1)
{
for(int i=0;i<=30;i++)
{
int now=(rx>>i)&1;
if(!bit[i])
{
if(now==0)
bit[i]=6;
else bit[i]=3;
}
else
{
if(now==0)
{
if(bit[i]==1||bit[i]==2)continue;
else if(bit[i]==4)bit[i]=2;
else if(bit[i]==5)bit[i]=1;
else if(bit[i]==3)
{
puts("0");return ;
}
}
else
{
if(bit[i]==1||bit[i]==2)
{
puts("0");return ;
}
else if(bit[i]==3)continue;
else if(bit[i]==4||bit[i]==5)bit[i]=3;
}
}
}
}
ll ans=1;
for(int i=0;i<=30;i++)
ans*=num[bit[i]];//cout<<i<<' '<<bit[i]<<endl;
printf("%lld\n",ans);
return ;
}
int main()
{
T=read();
while(T--)work();
return 0;
}
77B.集合论
unordered_set+加法标记即可。剩下基本就是模拟了。
为了避免取交集时清空使复杂度爆炸,可以采取一种清空STL的qjyq:新建一个空的然后$swap()$。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<unordered_set>
#include<vector>
using namespace std;//换dg的fread
char xch,xB[1<<15],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
inline int read()
{
int x=0,f=1;char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
typedef long long ll;
ll tot;
int m,type;
unordered_set<int> s;
vector<int> tmp;
ll add;
int main()
{
m=read();
while(m--)
{
int op=read();
if(op==1)
{
int num=read();
for(int i=1;i<=num;i++)
{
int x=read();
x-=add;
if(!s.count(x))tot+=x;
s.insert(x);
}
}
else if(op==2)
{
int num=read();tmp.clear();
for(int i=1;i<=num;i++)
{
int x=read();
if(s.count(x-add))tmp.push_back(x);
}
int sz=tmp.size();
unordered_set<int> swp;
swap(s,swp);
add=tot=0;
for(int i=0;i<sz;i++)
s.insert(tmp[i]),tot+=tmp[i];
}
else if(op==3)add++;
else if(op==4)add--;
printf("%lld\n",tot+add*1LL*s.size());
}
return 0;
}
78A.串串香
本来dg不想把题出的这么水的(看官方题解就知道了),但是大概是因为他太巨了所以忽视了Hash这种sb算法?
把头尾两段循环节拼起来跑一下,中间的全加上就好了。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ull base=13331;
const int N=2e6+5;
char s[N];
int n,m,T;
ull h[N],p[N];
ull get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
void work()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
h[0]=0;
for(int i=1;i<=m;i++)
h[i]=h[i-1]*base+s[i]-'A';
for(int i=1;i<=m;i++)
h[i+m]=h[i+m-1]*base+s[i]-'A';
ll ans=0;
if(n==1)
{
for(int i=1;i<m;i++)
if(get(1,i)==get(m-i+1,m))ans=max(ans,1LL*i);
if(!ans)puts("-1");
else printf("%lld\n",ans);
return ;
}
for(int i=m+1;i<2*m;i++)
if(get(1,i)==get(2*m-i+1,2*m))ans=max(ans,1LL*(i-m));
ans+=1LL*(n-1)*m;
printf("%lld\n",ans);
}
int main()
{
/* freopen("dt.in","r",stdin);
freopen("my.out","w",stdout);*/
scanf("%d",&T);
p[0]=1;
for(int i=1;i<=N-5;i++)
p[i]=p[i-1]*base;
while(T--)work();
return 0;
}
78C.木叶下
真的棒!!直接加深了我对倍增的理解。
显然,没有基环以及$u=v$的情况下答案就是直径/2+1。如果成环,答案就是环到树上其它点的最远距离。
由于每次询问给出连接的两点,我们可以直接在求这两点lca的过程中遍历环上所有的点。所以考虑倍增。
设$dp[x][]$表示x的父亲在不经过x的情况下能向下走多远。这东西显然是可以树形dp预处理,然后倍增的。
这样在倍增求LCA过程中沿途的点的贡献就可以得到了。但是比较麻烦的是最后到达LCA的时候,这时LCA的贡献有子树和头上两种来源,而子树的贡献是不能算u和v所在链的。所以我们还需要树形dp时求出子树内的前3长链,并记录他们分别属于哪个儿子。那么在倍增的最后分类讨论一下即可。
至于LCA头上的贡献,同样可以树形dp得到。
细节稍多。
#include<cstdio>
#include<iostream>
#include<cstring>
#define pa pair<int,int>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=2e5+5;
int n,m;
int to[N<<1],head[N],nxt[N<<1],tot,fa[N][22],dp[N][22],d[N],dep[N],up[N];
struct node
{
pa maxd,secd,thd;
node()
{
maxd.second=maxd.first=secd.second=secd.first=thd.second=thd.first=0;
}
}dis[N];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int f)
{
int maxd=-1,secd=-1,thd=-1,n1=-1,n2=-1,n3=-1;
int son=0;
fa[x][0]=f;dep[x]=dep[f]+1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
dfs(y,x);son++;
if(d[y]>=maxd)
{
thd=secd,secd=maxd,maxd=d[y];
n3=n2,n2=n1,n1=y;
}
else if(d[y]>=secd)
{
thd=secd,secd=d[y];
n3=n2,n2=y;
}
else if(d[y]>=thd)thd=d[y],n3=y;
}
d[x]=maxd+1;
//if(son==1){dp[n1][0]=0;return ;}
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
if(y==n1)dp[y][0]=secd+1;
else dp[y][0]=maxd+1;
}
if(maxd!=-1)
{
dis[x].maxd.second=maxd+1;
dis[x].maxd.first=n1;
}
if(secd!=-1)
{
dis[x].secd.second=secd+1;
dis[x].secd.first=n2;
}
if(thd!=-1)
{
dis[x].thd.second=thd+1;
dis[x].thd.first=n3;
}
}
void dfs2(int x,int f)
{
for(int i=1;i<=20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=1;i<=20;i++)
dp[x][i]=max(dp[x][i-1],dp[fa[x][i-1]][i-1]);
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)continue;
up[y]=max(up[x]+1,dp[y][0]+1);
dfs2(y,x);
}
}
int ask(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
int res=d[y],old=x;
for(int i=20;i>=0;i--)
if(dep[fa[y][i]]>=dep[x])res=max(res,dp[y][i]),y=fa[y][i];
if(x==y)return max(res,up[x]);
res=max(res,d[old]);
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])res=max(res,max(dp[x][i],dp[y][i])),x=fa[x][i],y=fa[y][i];
int lca=fa[x][0];
if(dis[lca].maxd.first!=x&&dis[lca].maxd.first!=y)return max(max(res,up[lca]),dis[lca].maxd.second);
else if(dis[lca].secd.first!=x&&dis[lca].secd.first!=y)return max(max(res,up[lca]),dis[lca].secd.second);
else return max(max(res,up[lca]),dis[lca].thd.second);
}
int main()
{
//freopen("dt.in","r",stdin);
//freopen("my.out","w",stdout);
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
dfs(1,0);
dfs2(1,0);
int ini=0;
for(int i=1;i<=n;i++)
ini=max(ini,dis[i].maxd.second+dis[i].secd.second);
ini=ini/2+1;
m=read();
for(int t=1;t<=m;t++)
{
int u=read(),v=read();
if(u==v){printf("%d\n",ini);continue;}
printf("%d\n",ask(u,v));
}
return 0;
}
87B.bird
让鸟飞比较困难,不如看成人往右走。
那么dp其实就很好想了,一个点的收益只能从它之前距离不小于K的点转移而来。
但是有一个问题:

RT。如果直接把当前点能射的鸟加上的话会有重复。可以采用队列的思想,把鸟按右端点递增排序,用队列维护影响到(即有重复覆盖)当前点i的鸟。每只鸟进队时将它的贡献-1,出队时恢复它的贡献。当然,考虑每只鸟入队比较麻烦,可以转移到一个点时把它的dp值减去以该点为左端点的鸟的个数,当然右端点直接维护一个指针就好了。
可以线段树维护。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+5,M=5e5+5;
struct node
{
int l,r;
friend bool operator < (const node &x,const node &y)
{
if(x.r==y.r)return x.l<y.l;
return x.r<y.r;
}
}a[N];
int n,maxr,dif[M],cov[M],K,beg[M],tmp;
int lz[M<<2],s[M<<2],dp[M],ans;
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
void down(int k)
{
if(!lz[k])return ;
lz[ls(k)]+=lz[k];s[ls(k)]+=lz[k];
lz[rs(k)]+=lz[k];s[rs(k)]+=lz[k];
lz[k]=0;
}
void change(int k,int l,int r,int L,int R,int val)
{
if(L<=l&&R>=r)
{
s[k]+=val;
lz[k]+=val;
return ;
}
down(k);
int mid=l+r>>1;
if(L<=mid)change(ls(k),l,mid,L,R,val);
if(R>mid)change(rs(k),mid+1,r,L,R,val);
s[k]=max(s[ls(k)],s[rs(k)]);
}
void update(int k,int l,int r,int pos,int val)
{
if(l==r){s[k]=val;return ;}
down(k);
int mid=l+r>>1;
if(pos<=mid)update(ls(k),l,mid,pos,val);
else update(rs(k),mid+1,r,pos,val);
s[k]=max(s[ls(k)],s[rs(k)]);
}
int ask(int k,int l,int r,int L,int R)
{
if(L<=l&&R>=r)return s[k];
down(k);
int mid=l+r>>1,res=0;
if(L<=mid)res=max(res,ask(ls(k),l,mid,L,R));
if(R>mid)res=max(res,ask(rs(k),mid+1,r,L,R));
return res;
}
int main()
{
n=read();K=read();
for(int i=1;i<=n;i++)
{
int l=max(0,read()),r=read();
if(r<0)continue;
l++;r++;
a[++tmp]=(node){l,r};
++dif[l];--dif[r+1];
beg[l]++;
maxr=max(maxr,r);
}
n=tmp;
sort(a+1,a+n+1);
int now=0;
for(int i=1;i<=maxr;i++)
now+=dif[i],cov[i]=now;
int top=0,p=1;
for(int i=1;i<=maxr;i++)
{
dp[i]=cov[i]+ask(1,0,maxr,max(0,i-K*2),max(0,i-K));
ans=max(ans,dp[i]);top+=beg[i];
update(1,0,maxr,i,dp[i]-top);
while(p<=n&&a[p].r==i)
{
top--;
change(1,0,maxr,a[p].l,a[p].r,1);
p++;
}
}
printf("%d\n",ans);
return 0;
}
84A.Smooth
似乎是个套路?维护B个队列,每次取出所有队首里最小的,将它乘上一个合法范围内的质数再塞回去。迭代K-1次即可得到结果。
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
const int pr[18]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
typedef long long ll;
const ll inf=2e18;
int B,K;
queue<ll> q[18];
int main()
{
scanf("%d%d",&B,&K);
for(int i=1;i<=B;i++)
q[i].push(pr[i]);
for(int i=2;i<K;i++)
{
ll minx=inf;int now=0;
for(int j=1;j<=B;j++)
if(!q[j].empty()&&q[j].front()<minx)minx=q[j].front(),now=j;
q[now].pop();
for(int j=1;j<=B;j++)
{
if(inf/pr[j]>minx)q[j].push(pr[j]*minx);
if(minx%pr[j]==0)break;
}
}
ll minx=inf;
for(int i=1;i<=B;i++)
if(!q[i].empty())minx=min(minx,q[i].front());
cout<<minx<<endl;
return 0;
}

浙公网安备 33010602011771号