[省选集训2022] 模拟赛15
Lost
题目描述
定义布尔函数 \(f(x)\) 表示 \(x\) 是否为完全平方数,给定 \(n,m\),求:
\(n,m\leq 10^{12}\)
解法
我们向深入考察 \(f(ij)\) 的性质,考虑 \(ij\) 为完全平方数的充要条件是,存在唯一的 \(d\) 满足 \(i=dx^2,j=dy^2\),考虑 \(d\) 是一个只包含一次质因子的数,所以可以枚举 \(d\):
考虑对 \(u(d)^2\) 进行变换,有等式 \(\sum_{i^2|d}\mu(i)=\mu(d)^2\),证明可以考虑如果 \(d\) 是完全平方数,那么 \(i\) 可以自由选择包不包含某个质数 \(p\),包含和不包含的 \(\mu\) 之和是相反数,所以抵消。而否则 \(i\) 只能取 \(1\),就和 \(\mu(d)^2\) 相等了。我们带入这个等式:
按照套路可以枚举 \(i\) 来化简这个式子:
注意根号的时候要强转 \(\tt ll\) 再做乘法才是符合组合意义的,外层可以直接枚举,内层整除分块,注意我们只对 \(\mu(i)\) 有值的位做整除分块,那么跑得就会非常快,其实是我算不来复杂度。
补充:直接对 \(d\) 不包含平方因子这个条件做莫比乌斯反演,也可以得到最后的式子。
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int M = 1000005;
#define int long long
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,m,cnt,ans,vis[M],p[M],mu[M];
void init(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt && i*p[j]<=n;j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
}
}
int work(int n,int m)
{
int res=0;
for(int l=1,r=1;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
res+=(r-l+1)*(int)sqrt(n/l)*(int)sqrt(m/l);
}
return res;
}
signed main()
{
freopen("lost.in","r",stdin);
freopen("lost.out","w",stdout);
n=read();m=read();init(1e6);
if(n>m) swap(n,m);
for(int i=1,t=sqrt(n);i<=t;i++) if(mu[i])
ans+=mu[i]*work(n/i/i,m/i/i);
printf("%lld\n",ans);
}
dignity
题目描述
给定 \(n\) 个数 \(a_i\),设 \(f(T)\) 表示,满足 \(\sum_{i=1}^n a_ic_i=T\) 的数组 \(c_i\) 的个数。
有 \(q\) 次询问,每次询问 \(h_i\),求出最小的 \(T\) 使得 \(f(T)\geq h_i\)
\(n\leq 5,a_i\leq10,q\leq 100,h_i\leq 10^{15}\)
解法
我竟然没有想到矩阵加速!考虑求出 \(f(T)\) 其实就是一个完全背包问题,我们设 \(dp(i,j)\) 表示使用 \(i\) 个数,其总和为 \(j\) 的方案数,那么我们对第二维矩阵加速即可,只需要保留 \(10\) 范围以内的 \(j\),所以矩阵大小是 \(50\times 50\) 的。
询问可以考虑倍增,最大方案数是具有单调性的,预处理次幂就可以做到 \(O(50^3\cdot \log h+q\cdot 50^2\cdot\log h)\)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 55;
#define int long long
#define zz __int128
const zz inf = 1e18;
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,m,k,a[M],id[M][M];
struct mat
{
int n,m,a[M][M];
mat(int A=0,int B=0) {n=A;m=B;memset(a,0,sizeof a);}
mat operator * (const mat &b) const
{
mat r=mat(n,b.m);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
for(int k=1;k<=b.m;k++)
{
zz t=min(inf,(zz)a[i][j]*b.a[j][k]);
r.a[i][k]=min(inf,r.a[i][k]+t);
}
return r;
}
}A[62],B;
signed main()
{
freopen("dignity.in","r",stdin);
freopen("dignity.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
for(int j=0;j<10;j++)
id[i][j]=++k;
A[0]=mat(k,k);B=mat(1,k);
for(int i=1;i<=n;i++)
a[i]=read(),B.a[1][id[i][9]]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<9;j++)
A[0].a[id[i][j+1]][id[i][j]]=1;
if(i>1) for(int j=1;j<=k;j++)
A[0].a[j][id[i][9]]=A[0].a[j][id[i-1][9]];
A[0].a[id[i][10-a[i]]][id[i][9]]=1;
}
for(int i=1;i<=60;i++) A[i]=A[i-1]*A[i-1];
m=read();
while(m--)
{
int x=read(),lim=100*x,ans=0;mat F=B;
for(int i=60;i>=0;i--) if(ans+(1ll<<i)<lim)
{
mat tmp=F*A[i];int mx=0;
for(int j=0;j<10;j++)
mx=max(mx,tmp.a[1][id[n][j]]);
if(mx<x) F=tmp,ans+=(1ll<<i);
}
if(x>1) ans++,F=F*A[0];int mx=0;
for(int j=0;j<10;j++)
mx=max(mx,F.a[1][id[n][j]]);
if(mx>=x) printf("%lld\n",ans);
else puts("What a pity!");
}
}
CF643G Choosing Ads
题目描述
解法
话说我当时看这题是紫的就把他跳过了,可能我刷再多题也做不到原题吧。
题目的提示已经很明显了,我们只需要找出一些满足必要条件的数即可。
考虑 \(p=51\) 的时候(也就是求区间的绝对众数),有一种方法是每次删去两个不同的数,那么如果区间存在绝对总数一定会存留到最后。
这种做法是可以推广的,设 \(k=\lfloor\frac{100}{p}\rfloor\),那么我们每次删除 \(k+1\) 个不同的数,不难证明可能成为答案的数一定会存留到最后。
这东西可以用线段树暴力维护,时间复杂度 \(O(n\log n\cdot k^2)\)
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 150005;
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,m,p,k,fl[M<<2];
struct node
{
int a[10],b[10];
void clear()
{
for(int i=0;i<p;i++) a[i]=b[i]=0;
}
node operator + (const node &o) const
{
node r=(*this);
for(int i=0;i<p;i++)//add o[i]
{
int fl=0;
for(int j=0;j<p;j++) if(o.a[i]==r.a[j])
{fl=1;r.b[j]+=o.b[i];break;}
if(fl) continue;
r.b[p]=o.b[i];
int x=p,mn=0;
for(int j=0;j<p;j++) if(r.b[j]<r.b[x]) x=j;
mn=r.b[x];
for(int j=0;j<p;j++) r.b[j]-=mn;
if(x<p) r.a[x]=o.a[i],r.b[x]=o.b[i]-mn;
}
return r;
}
void print()
{
printf("%d",p);
for(int i=0;i<p;i++)
printf(" %d",a[i]);
puts("");
}
}t[M<<2],ans;
void cov(int i,int len,int c)
{
fl[i]=c;t[i].clear();
t[i].a[0]=c;t[i].b[0]=len;
}
void down(int i,int l,int r)
{
if(!fl[i]) return ;
int mid=(l+r)>>1;
cov(i<<1,mid-l+1,fl[i]);
cov(i<<1|1,r-mid,fl[i]);fl[i]=0;
}
void build(int i,int l,int r)
{
if(l==r)
{
t[i].a[0]=read();t[i].b[0]=1;
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
t[i]=t[i<<1]+t[i<<1|1];
}
void add(int i,int l,int r,int L,int R,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {cov(i,r-l+1,c);return ;}
int mid=(l+r)>>1;down(i,l,r);
add(i<<1,l,mid,L,R,c);
add(i<<1|1,mid+1,r,L,R,c);
t[i]=t[i<<1]+t[i<<1|1];
}
void ask(int i,int l,int r,int L,int R)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {ans=ans+t[i];return ;}
int mid=(l+r)>>1;down(i,l,r);
ask(i<<1,l,mid,L,R);
ask(i<<1|1,mid+1,r,L,R);
}
signed main()
{
n=read();m=read();k=read();p=100/k;
build(1,1,n);
while(m--)
{
int op=read(),l=read(),r=read();
if(op==1) add(1,1,n,l,r,read());
else
{
ans.clear();
ask(1,1,n,l,r);
ans.print();
}
}
}