记录一些杂题,P5889 跳树,线段树+二进制好题。主要是要看出,一个点从根到自己的路径可以用二进制表示出来,
那向左右儿子转移的方式就是在自身后面添加,然后向父亲转移那就是上移一位。那么我们不妨重载运算符。那么分出几种情况讨论一下即可 ,然后我们记录一下哈,记录最上能到哪,从最上往最下走能到哪,这两个点之间的距离是多少。用于转移i。那么直接搞了啦,其实也不是,就大概懂了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,a[520001];
struct node
{
int l,r,lc,rc;
int top; //s最高会跳到它的第几辈祖先上
int ll; //跳到最高祖先后,s会跳到这个祖先的第几代孩子上(终点与最高祖先的深度差)
int num; //祖先-终点 的路径
friend node operator + (const node &x,const node &y)
{
if(!y.top&&!y.ll)
{
return x;
}
if(!x.top&&!x.ll)
{
return y;
}
node ans;
if(x.ll>y.top)
{
ans.top=x.top;
ans.ll=x.ll-y.top+y.ll;
ans.num=((x.num>>y.top)<<y.ll)+y.num;
}
else
{
ans.top=x.top+y.top-x.ll;
ans.ll=y.ll;
ans.num=y.num;
}
return ans;
}
};node e[2500001];
int bt(int x,int y)
{
int now=++len;
int l=x,r=y,lc=-1,rc=-1;
if(x==y)
{
if(a[x]==1) e[now].ll=1;
if(a[x]==2) e[now].ll=e[now].num=1;
if(a[x]==3) e[now].top=1;
}
else
{
int mid=(l+r)/2;
lc=bt(l,mid);rc=bt(mid+1,r);
e[now]=e[lc]+e[rc];
}
e[now].l=l,e[now].r=r;e[now].lc=lc,e[now].rc=rc;
return now;
}
void cg(int now,int x,int k)
{
int l=e[now].l,r=e[now].r;
if(l==r)
{
e[now].ll=e[now].top=e[now].num=0;
if(k==1) e[now].ll=1;
if(k==2) e[now].ll=e[now].num=1;
if(k==3) e[now].top=1;
e[now].l=l,e[now].r=r;
}
else
{
int lc=e[now].lc,rc=e[now].rc,mid=(l+r)/2;
if(x>=mid+1) cg(rc,x,k);
else cg(lc,x,k);
e[now]=e[lc]+e[rc];e[now].l=l,e[now].r=r;
e[now].lc=lc,e[now].rc=rc;
}
return ;
}
node find(int now,int x,int y)
{
int l=e[now].l,r=e[now].r;//printf(">>%lld %lld %lld %lld %lld\n",now,l,r,x,y);
if(l==x&&y==r) return e[now];
else
{
int lc=e[now].lc,rc=e[now].rc,mid=(l+r)/2;
if(x>=mid+1) return find(rc,x,y);
else if(y<=mid) return find(lc,x,y);
else return find(lc,x,mid)+find(rc,mid+1,y);
}
}
main()
{
int q;scanf("%lld%lld%lld",&n,&m,&q);
for(int i=1;i<=m;i++) scanf("%lld",&a[i]);
int root=bt(1,m);
while(q--)
{
int op;scanf("%lld",&op);
if(op==1)
{
int s,l,r;scanf("%lld%lld%lld",&s,&l,&r);
node ans=find(root,l,r);
s=(max(1ll,s>>ans.top)<<ans.ll)+ans.num;
printf("%lld\n",s);
}
else
{
int x,k;scanf("%lld%lld",&x,&k);
cg(root,x,k);
}
}
return 0;
}
或许还能在放一道luogu月赛的dp?我不放dp专栏是因为考虑至这一题的转移是我从未所见的,倒也不算难,只不过从思维方式上而言导致我推了三四个dp式都错了。P8848 [JRKSJ R5] 1-1 B,首先一部分的转移我是推出来了,也就-1数量>1数量的时候,但是后面考虑二维dp处理1>-1的时候,并未想到从某个位置前面0,1的和(前缀和)上处理问题,而是一昧果断的考虑用容斥来解决,从而导致了这次月赛的失利。其实是这种转移方式确实少见,若不是灵光一闪的话,那么考虑的方向便是发现每一个点的前缀和与后缀和必须要求大于0才能满足题目的要求吗,看来还是我菜了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[2000001],sum1=0,mod=998244353;
int inv[2000001],fc[2000001],f[3][2000001];
int power(int x,int k)
{
int rt=1;
while(k)
{
if(k&1) rt=((long long)rt*x)%mod;
x=((long long)x*x)%mod;k>>=1;
}
return rt;
}
void init()
{
fc[0]=1;
for(int i=1;i<=2000001;i++)fc[i]=(fc[i-1]*i)%mod;
inv[2000001]=power(fc[2000001],mod-2);
for(int i=2000001-1;i>=0;i--) inv[i]=(inv[i+1]*(i+1))%mod;
return ;
}
long long C(int i,int j)
{
return (fc[i]*inv[j]%mod*inv[i-j])%mod;
}
signed main()
{
scanf("%lld",&n);init();
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum1+=(a[i]==1);
if(sum1<=n-sum1)
{
int k=n-sum1,ans=C(k+1,sum1)%mod;//printf("%lld %lld\n",sum1,k+1);
printf("%lld",ans);
}
else
{
int k=n-sum1,kk=sum1-k,tmp=0;f[tmp][0]=1;
for(int i=1;i<=n;i++)
{
tmp^=1;
for(int j=1;j<=kk;j++)
{
f[tmp][j]=(f[tmp^1][j-1]+f[tmp^1][j+1])%mod;
}
f[tmp][0]=f[tmp^1][1]%mod;
}
printf("%lld",f[tmp][kk]);
}
return 0;
}
明天就学图论!想想想!