11 13 总结

记录一些杂题,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;
}

明天就学图论!想想想!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值