第八场
赛时3题,J构造和队友一起开的,A不会别的队教的,都写写吧(感觉j比a简单)
A:
知道要筛因数但是想破脑袋也不知道怎么筛的题。。
J:
构造题,对于每种情况构造出全零序列就可以。
E:
这题暴力筛因数是可以过的,但是需要常数小一点。只要发现了s(m)只有108种情况就可以开始暴力枚举(n-S(m))然后只要判断这个数的因数是否合法就行了,注意可能重复出现同一个因数所以不能重复算答案。但是赛时以为过不了没写,赛后参考题解补了这个筛质数的写法,跑得很快(200ms)主要快在筛因数的复杂度是d(n)而且分解质因数的复杂度也是log级别的(详见质因数相关的blog)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define fir first
#define sec second
#define mkp make_pair
const int maxn=2e6+10;
int ans=0,nw,pri[maxn],g[maxn],k;
bool vis[maxn];
vector<pii>vt[maxn];
map<int,int>mp;
int n;
void jud(int x){
int tmp=x;
int s=n%x;
int res=0;
while(x>0){res+=(x%10);x/=10;}
if(res==s)ans++;
}
void dfs(int c,int f){
if(c==vt[nw].size()){
if(mp[f]==0)jud(f),mp[f]=1;
return;
}
dfs(c+1,f);
int x=vt[nw][c].fir,num=vt[nw][c].sec;
//cout<<x<<' '<<num<<endl;
for(int i=1;i<=num;i++)f*=x,dfs(c+1,f);
return;
}
void solve(){
cin>>n;ans=0;mp.clear();
int l=max(1LL,n-109),r=n,L=r-l+1;
for(int i=1;i<=L;i++)g[i]=n-i+1,vt[i].clear();
for(int i=1;i<=k;i++){
int x=n%pri[i]+1;
if(x>L)continue;
for(int j=x;j<=L;j+=pri[i]){
int cnt=0;
while(g[j]%pri[i]==0){
g[j]/=pri[i];cnt++;
}
vt[j].push_back(mkp(pri[i],cnt));
}
}
for(int i=1;i<=L;i++)if(g[i]>1)vt[i].push_back(mkp(g[i],1));
for(int i=1;i<=L;i++)nw=i,dfs(0,1);
cout<<ans<<endl;
}
signed main(){
for(int i=2;i<=1e6;i++){
if(!vis[i]){
pri[++k]=i;
}
for(int j=1;j<=k&&pri[j]*i<=1e6;j++){
vis[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
int tt;cin>>tt;while(tt--){
solve();
}
}
I:
cdq分治题。
但是因为被修改操作强制在线了所以需要对分治的每个区间都维护一个线段树,并且在左区间维护最大值右区间维护最小值,只要存在最小值小于最大值就会符合条件了。这样维护和查询就是log方能过。
这题一个变种树套树,我没懂的地方主要是搞错了第二层线段树要维护的量。我本来只想到维护两个量,一个是以bi为下标ai小于等于mid的ci的最大值,以及以bi为下标ai大于mid的ci的最小值。但是这样查询就是n*m于是我就不会了。
应该先搞清楚这是一个单点修改整体查询的线段树,所以只要写一个upd函数没有别的函数了,并且要维护第三个量:是否存在左侧大于右侧,是为1,否为0。这样就可以log查询了。

浙公网安备 33010602011771号