[正睿集训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)\),则有:
后面的部分可以暴力卷起来,相当于是在模 \(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]);
}