• NOI ONLINE 2021题解


    NOI Online 2021 提高组 愤怒的小N

    description

    给出(n) 和一个(k-1) 次多项式(f(x)) ,求(sum_{i=0}^{n-1}[pop(i)equiv1mod 2]f(i))

    其中(pop(i)) 表示(i) 在二进制表示中(1) 的个数。

    data range

    (log_2nle 5 imes 10^5,kle 500)

    solution

    题解和官方题解如有雷同,纯属抄袭

    此题等于99%的汗水加上1%的灵感,但往往1%的灵感比99%的汗水更重要。

    来,我作为一个憨批,应该如何思考呢?

    肯定是先打表。考虑([0,16)) 符合条件的数有

    [1,2,4,7,8,11,13,14 ]

    容易发现恰好有(8) 个数((16) 的一半)

    根据题目所求,稍微往高次推推?

    [1+2+4+7+8+11+13+14=frac 12sum_{i=0}^{15}i\ 1^2+2^2+4^2+7^2+8^2+11^2+13^2+14^2=frac 12sum_{i=0}^{15}i^2\ 1^3+2^3+4^3+7^3+8^3+11^3+13^3+14^3=frac 12sum_{i=0}^{15}i^3\ ]

    妙哉!然而到了(4) 次方似乎就不对劲了。。。

    没关系。注意到(16=2^4) ,也就是说严格低于(2) 的幂次的次方应该都有这个结论。用数学语言表达下

    [Let g_{b,c}=sum_{i=0}^{2^b-1}(-1)^{pop(i)}i^c\ So when b>c,g_{b,c}=0 ]

    考虑证明这个结论。首先要有一个递推式,不难发现:

    [g_{b,c}=sum_{i=0}^{2^{b-1}-1}(-1)^{pop(i)}i^c-sum_{i=0}^{2^{b-1}-1}(-1)^{pop(i)}(i+2^{b-1})^c\ =g_{b-1,c}-sum_{i=0}^{2^{b-1}-1}(-1)^{pop(i)}sum_{c'=0}^cinom c{c'}i^{c'}(2^{b-1})^{c-c'}\ =g_{b-1,c}-sum_{c'=0}^cinom c{c'}(2^{b-1})^{c-c'}g_{b-1,c'} ]

    采用数学归纳法证明,归纳基础(b=0) 时结论显然成立,假设(b<h) 时结论均成立,下面对(b=h) 进行归纳:

    [For c<b-1,g_{b,c}=g_{b-1,c}-sum_{c'=0}^cinom c{c'}(2^{b-1})^{c-c'}g_{b-1,c'}=0-0=0\ For c=b-1,g_{b,c}=g_{b-1,b-1}-sum_{c'=0}^{b-1}inom c{c'}(2^{b-1})^{c-c'}g_{b-1,c'}=g_{b-1,b-1}-g_{b-1,b-1}=0 ]

    证毕。现在我们已经得到了一个很好的性质,让我们从头开始推,看能不能用上。

    [ans=frac 12sum_{i=0}^{n-1}(1-(-1)^{pop(i)})f(i)\ =frac 12sum_{i=0}^{n-1}f(i)-frac 12sum_{i=0}^{n-1}(-1)^{pop(i)}f(i) ]

    考虑减号前的式子,将(f(i)) 展开之后交换和式就是一个自然数幂和,这个东西怎么做都行,这里不再赘述。

    主要考虑后面的式子。我们想要让它尽量往(g_{b,c}) 上靠,但(n) 不一定是(2) 的正整数次幂。于是我们可以考虑(b_0<b_1<cdots<b_{t-1}) 满足

    [n=sum_{i=0}^{t-1}2^{b_i} ]

    然后类似数位(dp) 考虑第一位小于(n) 的位置,那么

    [=sum_{i=0}^{k-1}a_isum_{j=0}^{n-1}(-1)^{pop(j)}j^i\ =sum_{i=0}^{k-1}a_isum_{c=0}^{t-1}(-1)^{t-1-c}sum_{x=0}^{2^c-1}(-1)^{pop(x)}(x+sum_{j=c+1}^{t-1}2^{b_j})^i\ Let sum_c=sum_{j=c}^{t-1}2^{b_j},So\ =sum_{i=0}^{k-1}a_isum_{c=0}^{t-1}(-1)^{t-1-c}sum_{x=0}^{2^{b_c}-1}(-1)^{pop(x)}(x+sum_{c+1})^i\ =sum_{i=0}^{k-1}a_isum_{c=0}^{t-1}(-1)^{t-1-c}sum_{x=0}^{2^{b_c}-1}(-1)^{pop(x)}sum_{j=0}^iinom ijx^j(sum_{c+1})^{i-j}\ =sum_{i=0}^{k-1}a_isum_{c=0}^{t-1}(-1)^{t-1-c}sum_{j=0}^iinom ij(sum_{c+1})^{i-j}sum_{x=0}^{2^{b_c}-1}(-1)^{pop(x)}x^j\ =sum_{i=0}^{k-1}a_isum_{c=0}^{t-1}(-1)^{t-1-c}sum_{j=0}^iinom ij(sum_{c+1})^{i-j}g_{b_c,j}\ ]

    然后就可做了。根据性质,只有(mathcal O(k^2))(g) 需要处理,处理每一个需要(mathcal O(k)) ,这样是(mathcal O(k^3)) 的。然后枚举(i,c,j) 也只有(mathcal O(k^3)) (因为只用枚举(b_cle j)(c))。于是就做完了。

    time complexity

    (mathcal O(log_2n+k^3))

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7,N=5e5+5,K=505;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline void inc(int&x,int y){x=add(x,y);}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline void rec(int&x,int y){x=dec(x,y);}
    char ch[N];int n,k,a[K],pw[N],s[K],c[K][K],g[K][K],tmp[K],p[N],cnt,sum[N];
    inline int qpow(int x,int y)
    {
    	int ret=1;
    	for(;y;y>>=1,x=1ll*x*x%mod)
    		if(y&1)ret=1ll*ret*x%mod;
    	return ret;
    }
    int main()
    {
    	scanf("%s",ch+1);
    	n=strlen(ch+1);int now=0;
    	for(int i=1;i<=n;++i)
    	{
    		now=add(add(now,now),(ch[i]=='1'));
    		if(ch[i]=='1')p[cnt++]=n-i;
    	}
    	reverse(p,p+cnt);
    	scanf("%d",&k);
    	for(int i=0;i<k;++i)scanf("%d",a+i);
    	pw[0]=1;for(int i=1;i<=k;++i)pw[i]=1ll*pw[i-1]*now%mod;
    	c[0][0]=1;
    	for(int i=1;i<=k;++i)
    	{
    		c[i][0]=1;
    		for(int j=1;j<=i;++j)
    			c[i][j]=add(c[i-1][j],c[i-1][j-1]);
    	}
    	s[0]=now;
    	for(int i=1;i<k;++i)
    	{
    		s[i]=pw[i+1];
    		for(int j=0;j<i;++j)
    			rec(s[i],1ll*c[i+1][j]*s[j]%mod);
    		s[i]=1ll*s[i]*qpow(i+1,mod-2)%mod;
    	}
    	pw[0]=1;for(int i=1;i<=max(n,k);++i)pw[i]=add(pw[i-1],pw[i-1]);
    	g[0][0]=1;
    	for(int i=1;i<=k;++i)
    	{
    		tmp[0]=1;for(int j=1;j<=k;++j)tmp[j]=1ll*tmp[j-1]*pw[i-1]%mod;
    		for(int j=i;j<=k;++j)
    		{
    			g[i][j]=g[i-1][j];
    			for(int o=0;o<=j;++o)
    				rec(g[i][j],1ll*c[j][o]*tmp[j-o]%mod*g[i-1][o]%mod);
    		}
    	}
    	for(int i=cnt-1;~i;--i)sum[i]=add(sum[i+1],pw[p[i]]);
    	int ans=0;
    	for(int i=0;i<k;++i)
    	{
    		int res=0;
    		for(int j=0;j<cnt&&p[j]<=k;++j)
    		{
    			int s=sum[j+1],ret=0;
    			tmp[0]=1;for(int o=1;o<=i;++o)tmp[o]=1ll*tmp[o-1]*s%mod;
    			for(int o=p[j];o<=i;++o)
    				inc(ret,1ll*c[i][o]*tmp[i-o]%mod*g[p[j]][o]%mod);
    			((cnt-1-j)&1)?rec(res,ret):inc(res,ret);
    		}
    		rec(ans,1ll*a[i]*res%mod);
    	}
    	for(int i=0;i<k;++i)inc(ans,1ll*a[i]*s[i]%mod);
    	printf("%d
    ",1ll*qpow(2,mod-2)*ans%mod);
    	return 0;
    }
    

    NOI Online 2021 提高组 积木小赛

    description

    给出长度为(n) 的字符串(s,t)(t) 中本质不同的子串个数满足它是(s) 的一个子序列。

    data range

    (nle 3000)

    solution

    直接按照题意模拟即可。注意哈希表不要写错呜呜

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    const int N=3005,mod=1e5+7,base=101;
    int n;char s[N],t[N];
    struct ht
    {
    	int tot;vector<ull>v[mod+5];
    	inline void ins(ull p)
    	{
    		int tm=p%mod,las;
    		for(int i=0;i<v[tm].size();++i)
    			if(v[tm][i]==p)return;
    		v[tm].push_back(p);
    	}
    	inline bool query(ull p)
    	{
    		int tm=p%mod;
    		for(int i=0;i<v[tm].size();++i)
    			if(v[tm][i]==p)return true;
    		return false;
    	}
    }A;
    int main()
    {
    	scanf("%d",&n);
    	scanf("%s%s",s+1,t+1);
    	int ans=0;
    	for(int i=1;i<=n;++i)
    	{
    		ull now=0;int pos=1;
    		for(int j=i;j<=n;++j)
    		{
    			now=now*base+t[j]-'a'+1;
    			while(pos<=n&&s[pos]!=t[j])++pos;
    			if(pos>n)break;++pos;
    			if(A.query(now))continue;
    			++ans;A.ins(now);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    NOI Online 2021 提高组 岛屿探险

    description

    给出序列,每个位置(i) 有两个数(a_i,b_i) 。每次询问有四个参数(l_j,r_j,c_j,d_j) ,求(sum_{i=l_j}^{r_j}[a_ioplus c_jle min(c_j,b_i)])

    data range

    (nle 10^5,a_i,b_i,c_j,d_jle 2^{24}-1)

    solution

    先假设对于所有的询问都有(l_j=1,r_j=n) ,在此情况下考虑部分分做法。

    Part 1 (max{d_j}le min{b_i})

    此时(b) 可以直接忽略,对于所有的(a_i) 建立起一棵(01) 字典树,每次询问相当于给定(c,d) ,有多少(a_i) 可以满足上式。这个十分trivial ,直接分类讨论一下即可。

    Part 2 (min{d_j}ge max{b_i})

    此时(d) 可以直接忽略,但是仍然十分不好做。

    正难则反,考虑每一(a_i,b_i) 对于所有询问的贡献。

    于是可以先将所有询问的(c_j) 插入(01) 字典树中,每次给定(a,b) ,然后在相应节点上累加(tag) 代表在当前节点的子树中增加此次(a_i,b_i) 的贡献。做完后遍历(Trie) 树,下放标记就可以得到每个询问的答案了。

    现在回到原问题。考虑将每个询问区间差分,即([l,r] ightarrow [1,r]-[1,l-1])

    容易发现如果(i) 位置对询问([1,x_j]) 可能有贡献当且仅当(ile x_j) ,在此情况下只有当(b_i,d_j) 大小关系确定时我们才可以高效地计算答案。

    换句话说,只有满足一种类似二维偏序的关系时快速计算贡献才是可行的。

    于是我们可以将询问离线下来,然后将所有元素先一起按照(b_i or d_j) 从小到大排序。而后考虑计算贡献,采用(cdq) 分治的思想,在当前区间中,左右两边都按照原先岛屿的顺序进行排序,而后使用经典的(two pointer)的做法扫一遍即可。此时确定了(i,x_j)(b_i,d_j) 的大小关系,因此可以快速地计算贡献。

    time complexity

    (mathcal O(nlog_2^2n))

    code

    #include<bits/stdc++.h>
    namespace iobuff{
    	const int LEN=1000000;
    	char in[LEN+5], out[LEN+5];
    	char *pin=in, *pout=out, *ed=in, *eout=out+LEN;
    	inline char gc(void)
    	{
    		return pin==ed&&(ed=(pin=in)+fread(in, 1, LEN, stdin), ed==in)?EOF:*pin++;
    	}
    	inline void pc(char c)
    	{
    		pout==eout&&(fwrite(out, 1, LEN, stdout), pout=out);
    		(*pout++)=c;
    	}
    	inline void flush()
    	{ fwrite(out, 1, pout-out, stdout), pout=out; }
    	template<typename T> inline void scan(T &x)
    	{
    		static int f;
    		static char c;
    		c=gc(), f=1, x=0;
    		while(c<'0'||c>'9') f=(c=='-'?-1:1), c=gc();
    		while(c>='0'&&c<='9') x=10*x+c-'0', c=gc();
    		x*=f;
    	}
    	template<typename T> inline void putint(T x, char div)
    	{
    		static char s[15];
    		static int top;
    		top=0;
    		x<0?pc('-'), x=-x:0;
    		while(x) s[top++]=x%10, x/=10;
    		!top?pc('0'), 0:0;
    		while(top--) pc(s[top]+'0');
    		pc(div);
    	}
    }
    using namespace iobuff;
    using namespace std;
    const int N=2e5+5,LOG=24;
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    int n,q,cnt,ans[N];
    struct quy{bool tp1;int p,c,d,tp2,id;}a[N<<1],tmp[N<<1];
    inline bool cmp(const quy&x,const quy&y){return x.d<y.d;}
    struct Trie
    {
    	int rt,tot,ch[N<<5][2],sz[N<<5],tag[N<<5];
    	inline void pre(){rt=tot=0;}
    	inline int nd()
    	{
    		int p=++tot;ch[p][0]=ch[p][1]=0;
    		sz[p]=tag[p]=0;return p;
    	}
    	inline void ins(int num)
    	{
    		if(!rt)rt=nd();
    		int now=rt;
    		for(int i=LOG;~i;--i)
    		{
    			++sz[now];int bit=(num>>i)&1;
    			if(!ch[now][bit])ch[now][bit]=nd();
    			now=ch[now][bit];
    		}
    		++sz[now];
    	}
    	inline int del(int num)
    	{
    		int now=rt;
    		for(int i=LOG;~i;--i)
    		{
    			--sz[now];int bit=(num>>i)&1;
    			tag[ch[now][0]]+=tag[now],tag[ch[now][1]]+=tag[now];tag[now]=0;
    			now=ch[now][bit];
    		}
    		--sz[now];return tag[now];
    	}
    	inline int query(int c,int d)
    	{
    		int now=rt,ans=0;
    		for(int i=LOG;~i&&now;--i)
    		{
    			int bit=(c>>i)&1;
    			if((d>>i)&1)
    				ans+=sz[ch[now][bit]],now=ch[now][bit^1];
    			else now=ch[now][bit];
    		}
    		if(now)ans+=sz[now];
    		return ans;
    	}
    	inline void update(int c,int d)
    	{
    		int now=rt;
    		for(int i=LOG;~i&&now;--i)
    		{
    			int bit=(c>>i)&1;
    			if((d>>i)&1)
    				++tag[ch[now][bit]],now=ch[now][bit^1];
    			else now=ch[now][bit];
    		}
    		if(now)++tag[now];
    	}
    }A,B;
    inline void upd1(int i)
    {
    	if(a[i].tp1)ans[a[i].id]+=a[i].tp2*A.query(a[i].c,a[i].d);
    	else B.update(a[i].c,a[i].d);
    }
    inline void upd2(int j)
    {
    	if(a[j].tp1)ans[a[j].id]+=a[j].tp2*B.del(a[j].c);
    	else A.ins(a[j].c);
    }
    void cdq(int l,int r)
    {
    	if(l>=r)return;
    	int mid=(l+r)>>1;
    	cdq(l,mid),cdq(mid+1,r);
    	int i=l,j=mid+1,pos=l-1;
    	A.pre(),B.pre();
    	for(;j<=r;++j)if(a[j].tp1)B.ins(a[j].c);
    	j=mid+1;
    	while(i<=mid&&j<=r)
    	{
    		if(a[i].p<a[j].p||(a[i].p==a[j].p&&!a[i].tp1&&a[j].tp1))
    			upd1(i),tmp[++pos]=a[i++];
    		else upd2(j),tmp[++pos]=a[j++];
    	}
    	while(i<=mid)upd1(i),tmp[++pos]=a[i++];
    	while(j<=r)upd2(j),tmp[++pos]=a[j++];
    	for(i=l;i<=r;++i)a[i]=tmp[i];
    }
    int main()
    {
    	scan(n),scan(q);
    	for(int i=1,c,d;i<=n;++i)
    	{
    		scan(c),scan(d);
    		a[++cnt]={0,i,c,d,0,0};
    	}
    	for(int i=1,l,r,c,d;i<=q;++i)
    	{
    		scan(l),scan(r),scan(c),scan(d);
    		if(l>1)a[++cnt]={1,l-1,c,d,-1,i};
    		a[++cnt]={1,r,c,d,1,i};
    	}
    	sort(a+1,a+cnt+1,cmp);
    	cdq(1,cnt);
    	for(int i=1;i<=q;++i)putint(ans[i],'
    ');flush();
    	return 0;
    }
    
    NO PAIN NO GAIN
  • 相关阅读:
    BZOJ1941Hide and Seek
    数学与树学(娱乐向)
    树状数组(模板)
    BZOJ2716天使玩偶
    BZOJ3262陌上花开
    BZOJ3781小B的询问
    BZOJ3236作业
    Bsgs模板
    HNOI2008明明的烦恼
    BZOJ1211树的计数
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14590571.html
Copyright © 2020-2023  润新知