1003 Pty loves lines
解题思路
最后一段连续的可行答案其实很长,记录一下对于每个 i i i来说最后一段连续的起始位置然后暴力转移.
代码
#include<bits/stdc++.h>
#define ll long long
#define MAXN 1001001
#define MOD 998244353
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int vis[MAXN],las[MAXN];
vector<int> ans[750];
int main()
{
ans[1].pb(0);
rep(i,2,700)
{
ans[i].pb(0);las[i]=las[i-1]+i-1;
rep(j,1,i-1) for(auto x:ans[j]) if(!vis[x+j*(i-j)]&&(x+j*(i-j)<=las[i]))
{ans[i].pb(x+j*(i-j));vis[x+j*(i-j)]=1;}
sort(ans[i].begin(),ans[i].end());
dwn(j,ans[i].size()-1,0)
if(j==ans[i].size()-1||ans[i][j]+1==ans[i][j+1]) las[i]=ans[i][j];
else break;
for(auto x:ans[i]) vis[x]=0;
}
rep(T,1,read())
{
int n=read();
for(auto x:ans[n]) if(x>las[n]) break;else printf("%d ",x);
rep(i,las[n]+1,n*(n-1)/2) printf("%d%c",i,i==n*(n-1)/2?'\n':' ');
}
}
1004 Pty hates prime numbers
解题思路
转载自此处
考虑最经典的容斥
枚举每个数选还是不选,那么 a n s + = ( n / S ) × ( − 1 ) t h e n u m b e r o f s e l e c t e d n u m b e r ans+=(n/S)×(−1)^{the number of selected number} ans+=(n/S)×(−1)thenumberofselectednumber
即不断地加上选奇数个,减去选偶数个
复杂度为 O ( 2 k ) O(2^k) O(2k)
由于t比较大,这样显然过不了
考虑当k=8时,其乘积 S = 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 ∗ 19 = 9699690 S= 2∗3∗5∗7∗11∗13∗17∗19 = 9699690 S=2∗3∗5∗7∗11∗13∗17∗19=9699690
那么 a n s ( n ) = a n s ( 9699690 ) × ⌊ n 9699690 ⌋ + a n s ( n m o d 9699690 ) ans(n)=ans(9699690)×⌊\frac n{9699690}⌋+ans(n\,\,mod \,\,9699690) ans(n)=ans(9699690)×⌊9699690n⌋+ans(nmod9699690)
预处理 1 − 9699690 1-9699690 1−9699690即可 O ( 1 ) O(1) O(1)查询 n 1 个 + n 2 个 ⋯ − n 7 个 + n 8 个 \frac {n} {1个}+\frac {n} {2个}⋯−\frac {n} {7个}+\frac {n} {8个} 1个n+2个n⋯−7个n+8个n
对于 k > 8 k>8 k>8的情况,可以 O ( 2 k − 8 ) O(2k−8) O(2k−8)算出第 9 9 9到第 k k k个质数的情况,考虑前后相交的情况
显然有 n i ∗ j = n i j \frac n{i*j}=\frac{\frac n i}{j} i∗jn=jin
假设我们枚举后面一段的乘积为 r e s res res
与前面关联后我们需要求 − n 1 个 ∗ r e s + n 2 个 ∗ r e s ⋯ − n 7 个 ∗ r e s + n 8 个 ∗ r e s -\frac {n} {1个*res}+\frac {n} {2个*res}⋯−\frac {n} {7个*res}+\frac {n} {8个*res} −1个∗resn+2个∗resn⋯−7个∗resn+8个∗resn,再根据组合后的奇偶判断符号
将 r e s res res放到分子上,已知我们可以 O ( 1 ) O(1) O(1)去求 a n s ( n r e s ) = n r e s − n 1 个 ∗ r e s + n 2 个 ∗ r e s ⋯ − n 7 个 ∗ r e s + n 8 个 ∗ r e s ans(\frac n{res})=\frac n {res}-\frac {n} {1个*res}+\frac {n} {2个*res}⋯−\frac {n} {7个*res}+\frac {n} {8个*res} ans(resn)=resn−1个∗resn+2个∗resn⋯−7个∗resn+8个∗resn
即可以在枚举后半部分时, O ( 1 ) O(1) O(1)处理前后关系
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=9699690;
int a[1<<17|1],ans1=0,prime[100],bj[110],cnt;
long long c[2*maxn+50];
struct tnode{
int l,r;
}e[1<<17|1];
bool cmp(tnode l,tnode r)
{
return l.l<r.l;
}
long long b[1<<17|1];
void get_prime(int x)
{
for(int i=2;i<=x;i++)
{
if(!bj[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&prime[j]*i<=x;j++)
{
bj[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int cal(int x)
{
int ans=0;
while(x)
{
ans++;
x-=(x)&(-x);
}
return ans;
}
long long get_sum(long long n)
{
return c[9699690]*(n/9699690)+c[n%9699690];
}
int main()
{
get_prime(100);
int t;
scanf("%d",&t);
for(int i=1;i<=8;i++)
{
a[1<<(i-1)]=prime[i];
}
for(int i=1;i<(1<<8);i++)
{
for(int j=0;j<8;j++)
{
if((i&(1<<j))==0)
{
a[i|(1<<j)]=a[i]*prime[j+1];
}
}
}
for(int i=1;i<=maxn;i++)
c[i]=1;
for(int i=1;i<(1<<8);i++)
{
if(cal(i)&1)
{
for(int j=1;a[i]*j<=maxn;j++)
{
c[a[i]*j]-=j;
c[a[i]*(j+1)]+=j;
}
}
else
{
for(int j=1;a[i]*j<=maxn;j++)
{
c[a[i]*j]+=j;
c[a[i]*(j+1)]-=j;
}
}
}
for(int i=1;i<=maxn;i++)
c[i]+=c[i-1];
for(int i=1;i<=8;i++)
{
b[1<<(i-1)]=prime[i+8];
}
for(int i=1;i<(1<<8);i++)
{
for(int j=0;j<8;j++)
{
if((i&(1<<j))==0)
{
b[i|(1<<j)]=b[i]*prime[j+9];
}
}
}
while(t--)
{
int k;long long n;
scanf("%lld%d",&n,&k);
if(k<=8)
{
long long ans=n;
for(int i=1;i<(1<<k);i++)
{
if(cal(i)&1)ans-=n/a[i];
else ans+=n/a[i];
}
printf("%lld\n",ans);
}
else
{
k-=8;
long long ans=n;
ans=ans+(get_sum(n)-n);
for(int i=1;i<(1<<k);i++)
{
if(cal(i)&1)
{
ans-=n/b[i];
ans-=(get_sum(n/b[i])-n/b[i]);
}
else
{
ans+=n/b[i];
ans+=(get_sum(n/b[i])-n/b[i]);
}
}
printf("%lld\n",ans);
}
}
return 0;
}