[正睿集训2021] 模拟赛4

整数对

题目描述

求满足 \(0\leq x\leq n,0\leq y\leq\sqrt{\frac{p}{q}}\cdot x\)\((x,y)\) 的整数对个数。

\(1\leq T\leq 10^5,1\leq p,q\leq 1000,1\leq n\leq 10^9\)

解法

太棒啦,一道题掌握两个高科技。

这个 \(\sqrt{\frac{p}{q}}\) 有点烦,我们用 Stern-Brocot树 来把这个东西拟合成有理数 \(\frac{a}{b}\),我的方式是如果拟合出来的有理数要爆出 \(1e17\) 了那么我们退出拟合,我们再限定一个拟合次数 \(5000\),如果超过这个次数我们就认为精度够了。

剩下的问题变成了求 \(\sum_{x=0}^n\lfloor\frac{ax}{b}\rfloor\),直接搞个类欧几里得就行了。

#include <cstdio>
#include <cmath>
#define int __int128
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int T,p,q,n,t;
int work(int n,int a,int b,int c)
{
	int ac=a/c,bc=b/c,n1=n+1,m=(a*n+b)/c,f=0;
	if(!a) return bc*n1;
	if(a>=c || b>=c)
	{
		f=n*n1/2*ac+n1*bc+work(n,a%c,b%c,c);
		return f;
	}
	f=n*m-work(m-1,c,c-b-1,a);
	return f;
}
void find(int a,int b,int c,int d)
{
	t++;
	if(c>=1e17 || d>=1e17 || t>=5000)
	{
		write(work(n,a,0,b)+n+1);
		puts("");
		return ;
	}
	int m=a+c,n=b+d;
	if(n*n*p<m*m*q) find(a,b,m,n);
	else find(m,n,c,d); 
}
signed main()
{
	T=read();
	while(T--)
	{
		t=0;
		p=read();q=read();n=read();
		find(0,1,1,0);
	}
}

序列变换

题目描述

对于一个长度为 \(n\),下标为 \([0,n)\) 的非负整数序列 \(a\),定义变换 \(b=f(a,k)\),其中 \(b\) 也是长度为 \(n\) 的序列,且 \(b_i=\oplus_{j=0}^{k-1}a_{(i+j)\bmod n}\),问变换 \(T\) 次之后得到的序列。

\(1\leq k\leq n\leq5\cdot 10^5,1\leq T\leq 10^{18}\)

解法

这道题变换其实有点复杂,算贡献这个方法是行不通的。

不妨从卷积的角度来理解这个变换,定义乘法为异或起来的卷积,设初始序列的生成函数是 \(F(x)\),则有:

\[F(x)\times (1+x...+x^{k-1})^T\bmod (x^n-1) \]

后面的部分可以暴力卷起来,相当于是在模 \(2\) 意义下的卷积,但是 \(O(n\log k\log n)\) 直接 \(\tt T\) 飞了。

有一个神奇结论:\((1+x...+x^{k-1})^{2^t}=1+x^{2^t}...+x^{(k-1)2^t}\),这个可以归纳证明,不是自己乘自己的项的系数都是 \(2\) 的倍数,所以都被模掉了,然后一直归纳上来即可。

\(F(x)\times(1+x...+x^{k-1})^T\bmod(x^n-1)\) 是可以 \(O(n)\) 算的,就把每一类的东西取出来,然后做一个类似前缀和的东西就行了,讨论一下 \(k\) 大于整类的情况,所以直接二进制拆分 \(O(n\log T)\)

#include <cstdio>
#include <vector>
using namespace std;
#define int long long
const int M = 500005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,t,a[M],b[M],vis[M];vector<int> v;
void work(int x)
{
	for(int i=0;i<n;i++)
	{
		v.clear();
		if(!vis[i])
		{
			//取出这一类所有的元素
			int ans=0;
			for(int j=i;!vis[j];j=(j+x)%n)
				vis[j]=1,v.push_back(j),ans^=a[j];
			if((k/v.size())%2==0) ans=0;//否则可以取遍整类 
			int u=k%v.size();
			for(int j=0;j<u;j++)
				ans^=a[v[j]];
			for(int j=0;j<v.size();j++)
			{
				b[v[j]]=ans;
				ans^=a[v[j]];
				ans^=a[v[(j+u)%v.size()]];
			}
		}
	}
	for(int i=0;i<n;i++) a[i]=b[i],b[i]=vis[i]=0;
}
signed main()
{
	n=read();k=read();t=read();
	for(int i=0;i<n;i++)
		a[i]=read();
	for(int p=1;t;p<<=1)
		if(t&p)
		{
			t^=p;
			work(p%n);
		}
	for(int i=0;i<n;i++) printf("%d ",a[i]);
}
posted @ 2021-03-28 21:56  C202044zxy  阅读(103)  评论(0编辑  收藏  举报