【ZJOI2017】【UOJ292】【LOJ2252】【洛谷P3689】多项式(暴力)(数学推导)

UOJ传送门

LOJ传送门

洛谷传送门

垃圾BZOJ不知道是不是放错数据了,居然连标算都A不了


题解:

这道题最扯的地方在于,除了IO优化,没有任何板子可以拿上来。

也就是说,这是一道纯思维难度加代码难度的题。与你板子熟不熟练没有任何关系。

然而推出结论后,正解相当暴力。

m x = max ⁡ ( n , k ) mx=\max(n,k) mx=max(n,k),则我们维护所有 2 m x 2^{mx} 2mx个不同的长为 m x mx mx 01 01 01串在这个大串里面的出现次数。

发现每次平方就是把所有项的次数乘上 2 2 2,因为交叉项会正反算两次,模2意义下就没有贡献了。

然后我就不会了。。。滚去orz标算了

在这里插入图片描述
但是我也不知道我哪里写挂掉了,需要对于询问前缀长度小于 n n n的情况进行特殊处理,不然过不了UOJ的hack数据。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc getchar
#define cs const
#define int ll
template<typename T>
inline T get(){
	char c;
	while(!isdigit(c=gc()));T num=c^48;
	while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
	return num;
}
inline int getint(){return get<int>();}
inline ll getll(){return get<ll>();}
inline int gs(){char c;while(!isdigit(c=gc()));return c^48;}

using std::cerr;
using std::cout;

cs int N=1<<19|1;

int t1[N],t2[N],T1[N],T2[N];
ll F[N],G[N],S,SS; 

int n,k,mx;
ll m,L,R;
int start,token;

bool st[60];int top;
ll Lim[60];
bool a[N],b[N],c[N];
inline ll solve(ll lim){
	if(lim==-1)return 0;
	std::bitset<128> o=start,p,t;o<<=mx+1;
	ll all=n,*f=F,*g=G;
	int *d1,*d2,del;bool R;
	if(lim<=n){
		int n=::n,token=::token;ll m=::m;
		a[all=0]=1;token>>=mx-k;
		for(int re i=0;i<=n;++i)b[i]=(start>>i)&1;
		while(m){
			if(m&1){
				for(int re i=all+n;~i;--i)c[i]=0;
				for(int re i=0;i<=all;++i)
				for(int re j=0;j<=n;++j)c[i+j]^=a[i]&b[j];
				all=std::min(all+n,lim);
				for(int re i=0;i<=lim;++i)a[i]=c[i];
			}
			for(int re i=n<<1;i>n;--i)b[i]=0;
			for(int re i=n<<1;~i;--i)b[i]=i&1?0:b[i>>1];
			n=std::min(n<<1,(int)lim);
			m>>=1;
		}
		int ans=0;
		for(int re i=0;i<=all;++i){
			ll p=0;
			for(int re j=0;j<=k;++j){
				p=p<<1|(i<j?0:a[i-j]);
			}
			ans+=p==token;
		}
		return ans;
	}
	Lim[1]=lim;
	for(int re i=2;i<60;++i)Lim[i]=Lim[i-1]+1>>1;
	for(int re i=0;i<=all;++i)++f[start<<(mx-i)&S];
	ll tmp=m;top=0;
	while(tmp>1)st[++top]=tmp&1,tmp>>=1;
	while(top){
		std::swap(f,g);lim=Lim[top];
		(R=st[top--])?(d1=T1,d2=T2):(d1=t1,d2=t2);
		for(int re s=0;s<=S;++s)if(g[s])f[d1[s]]+=g[s],f[d2[s]]+=g[s],g[s]=0;
		p=0;for(int re i=0;i<=(mx<<1|1);++i)p[i<<1]=o[i];
		all=(all<<1)+(R?mx:0);del=std::max(0ll,all-lim);
		switch(R){
			case 1:{o=0;
				for(int re i=0;i<=mx;++i)if(start&(1<<i))o^=p<<i;
				for(int re i=1;i<=mx;++i){
					t=o>>((mx<<2)+3-i);t&=S;
					f[t.to_ulong()]++;
				}
				for(int re i=0;i<del;++i){
					t=o>>((mx<<2)+2-i);t&=S;
					f[t.to_ulong()]--;
				}
				(o>>=mx*3+1-del)&=SS;
				break;
			}
			case 0:{
				for(int re i=0;i<del;++i){
					t=p>>(mx*3+2-i);t&=S;
					f[t.to_ulong()]--;
				}
				o=p>>((mx<<1|1)-del);o&=SS;
				break;
			}
		}
		--f[0];
		all-=del;
	}
	ll ans=0;
	for(int re i=0;i<(1<<mx-k);++i)ans+=f[i|token];
	for(int re s=0;s<=S;++s)f[s]=0;
	return ans;
}

inline void solve(){
	n=getint(),m=getll();
	k=getint()-1,R=n*m-getll()+1,L=n*m-getll()+1;
	mx=std::max(n,k),S=(1<<mx+1)-1,SS=(1ll<<(mx+1<<1))-1;
	start=token=0;
	for(int re i=0;i<=n;++i)start|=gs()<<mx-i;
	for(int re i=0;i<=k;++i)token|=gs()<<mx-i;
	if(R-L+1<k){
		cout<<"0\n";
		return ;
	}
	for(int re s=0;s<=S;++s){
		ll to=0,p=0;
		for(int re i=0;i<=mx;++i)to|=(s&(1ll<<i))<<i;
		t1[s]=to>>(mx-1)&S;
		t2[s]=to>>mx&S;
		for(int re i=0;i<=mx;++i)if(start&(1<<i))p^=to<<i;
		T1[s]=p>>(mx-1)&S;
		T2[s]=p>>mx&S;
	}
	ll ans1=solve(R);
	ll ans2=solve(L-1+k);
	cout<<ans1-ans2<<"\n";
}

signed main(){
#ifdef zxyoi
	freopen("poly.in","r",stdin);//freopen("poly.out","w",stdout);
#endif
	int T=getint();
	while(T--)solve();
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值