• IOI2021集训队作业 LC201 Cumulative Code


    给出(k),记树高为(k)满二叉树((2^k-1)个节点)的prufer序(i)个为(p_i),若干个询问,每次询问(a,d,m)表示求(sum_{i=0}^{m-1}p_{a+di})

    (kle 30,Qle 300)

    时间(7s)


    做题的时候只枚举了(kle 4)的prufer序,找到错误规律……

    看到(Qle 300)和那么多的时间,就应该发觉这不是数学题而是爆搜题。

    首先分析一下这个prufer序如何构造:显然是对根的左子树进行后序遍历,往prufer序中输出遍历到的每个点的父亲,然后遍历根,然后递归到右子树的子问题。

    定义(F(x,k))(G(x,k))分别表示根没有父亲和根有父亲的序列,序列中的每个元素用多项式表示。(x)表示根节点是什么。

    那么有:(F(x,k)=G(2x,k-1)+{x}+{2x+1}+F(2x+1,k-1))(G(x,k)=G(2x,k-1)+{x}+G(2x+1,k-1)+{x})

    如果没有推出通项,题目中的这条东西不太好求。于是可以考虑暴力,以(2^b)分一块计算。每一块都有询问((a,d,m)),在这块内计算出多项式之后用这一块的根节点的值代进去。

    由于多项式是一样的,所以可以一起算,只算两种块:(F(x,b))(G(x,b))。可以(O(2^bb))(O(2^b))地求出块内每个数,然后(O(2^{2b}))地做个前缀和,这样每个块就可以(O(1))询问了。询问的时候要拆成(2^{k-b})个块。于是时间为(O(2^{2b}+Q2^{k-b}))

    因为不想把空间开太大,所以我的程序中(b)取了(10)


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    #define B 10
    #define ll long long
    int k,b,Q;
    struct poly{
    	ll p,q;
    	poly(ll _p=0,ll _q=0){
    		p=_p,q=_q;
    	}
    	poly f(poly g){
    		return poly(p*g.p,p*g.q+q);
    	}
    	ll f(ll x){
    		return p*x+q;
    	}
    };
    poly operator+(poly a,poly b){return poly(a.p+b.p,a.q+b.q);}
    poly operator-(poly a,poly b){return poly(a.p-b.p,a.q-b.q);}
    int len(int k){return (1<<k)-2;}
    poly queryg(int x,int k){
    	if (k==2){
    		assert(x==1 || x==2);
    		return poly(1,0);
    	}
    	if (x<=len(k-1)) return queryg(x,k-1).f(poly(2,0));
    	if (x==len(k-1)+1) return poly(1,0);
    	if (x<=len(k-1)+1+len(k-1)) return queryg(x-len(k-1)-1,k-1).f(poly(2,1));
    	return poly(1,0);
    }
    poly queryf(int x,int k){
    	if (k==2){
    		assert(x==1);
    		return poly(1,0);
    	}
    	if (x<=len(k-1)) return queryg(x,k-1).f(poly(2,0));
    	if (x==len(k-1)+1) return poly(1,0);
    	if (x==len(k-1)+2) return poly(2,1);
    	return queryf(x-len(k-1)-2,k-1).f(poly(2,1));
    }
    struct data{
    	poly h[1<<B],s[1<<B][1<<B];
    	void init(poly query(int,int),int n){
    		for (int i=1;i<=n;++i){
    			h[i]=query(i,b);
    	//		printf("(%lld,%lld)
    ",h[i].p,h[i].q);
    		}
    	//	printf("
    ");
    		for (int j=1;j<n;++j){
    			for (int i=n-j+1;i<=n;++i)
    				s[j][i]=h[i];	
    			for (int i=n-j;i>=1;--i)
    				s[j][i]=s[j][i+j]+h[i];
    	//		for (int i=1;i<=len(b);++i)
    	//			printf("(%lld,%lld) ",s[j][i].p,s[j][i].q);
    	//		printf("
    ");
    		}
    	}
    } F,G;
    ll ans;
    void calc(int c,int a,int d,int m,int t){
    	if ((c-a)%d) return;
    	int tmp=(c-a)/d;
    	if (0<=tmp && tmp<=m)
    		ans+=t;
    }
    void divideg(int k,int a,int d,int m,int t){
    	if (k==b){
    		ans+=(m==0?G.h[a]:(G.s[d][a]-(a+(m+1)*d<=len(b)?G.s[d][a+(m+1)*d]:poly(0,0)))).f(t);
    		return;
    	}
    	calc(len(k-1)+1,a,d,m,t);
    	calc(len(k-1)+1+len(k-1)+1,a,d,m,t);
    	if (a+d*m==len(k-1)+1+len(k-1)+1){
    		if (m==0) return;
    		m--;
    	}
    	if (a<=len(k-1))
    		divideg(k-1,a,d,min(m,(len(k-1)-a)/d),t*2);
    	if (a+d*m>len(k-1)+1){
    		int tmp=(a>len(k-1)+1?0:(len(k-1)+1-a+1 +d-1)/d);
    		divideg(k-1,a+tmp*d-len(k-1)-1,d,m-tmp,t*2+1);
    	}
    }
    void dividef(int k,int a,int d,int m,int t){
    	if (k==b){
    		ans+=(m==0?F.h[a]:(F.s[d][a]-(a+(m+1)*d<=len(b)?F.s[d][a+(m+1)*d]:poly(0,0)))).f(t);
    		return;
    	}
    	calc(len(k-1)+1,a,d,m,t);
    	calc(len(k-1)+2,a,d,m,t*2+1);
    	if (a<=len(k-1))
    		divideg(k-1,a,d,min(m,(len(k-1)-a)/d),t*2);
    	if (a+m*d>len(k-1)+2){
    		int tmp=(a>len(k-1)+1?0:(len(k-1)+2-a+1 +d-1)/d);
    		dividef(k-1,a+tmp*d-len(k-1)-2,d,m-tmp,t*2+1);
    	}
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	scanf("%d%d",&k,&Q);
    	b=min(B,k);
    	G.init(queryg,(1<<b)-2);
    	F.init(queryf,(1<<b)-3);
    //	for (int i=1;i<=(1<<k)-3;++i){
    //		ans=0;
    //		dividef(k,i,1,0,1);
    //		printf("%lld
    ",ans);
    //	}
    //	return 0;
    	while (Q--){
    		int a,d,m;
    		scanf("%d%d%d",&a,&d,&m);
    		assert(d);
    		ans=0;
    		dividef(k,a,d,m-1,1);
    //		ll sum=0;
    //		for (int i=0;i<m;++i)
    //			sum+=queryf(a+d*i,k).f(1);
    //		assert(ans==sum);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    【转】职场中如何才能学会心理自控
    Gof《设计模式》完结
    阿里巴巴CEO马云最新超经典哲学语录
    大话设计模式感悟(3)——策略模式(Strategy)
    设计模式 ( 十三 ) 命令模式Command(对象行为型)
    数据结构&算法实践Python——序章
    设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
    设计模式 ( 十六 ) 观察者模式Observer(对象行为型)
    金融系列2《借贷记卡应用》
    php 设计模式数据映射模式(应用程序与数据库交互模式)
  • 原文地址:https://www.cnblogs.com/jz-597/p/13957006.html
Copyright © 2020-2023  润新知