新的阶乘
考虑从这个式子下手,怎么更优秀的求得答案。
那么我们只需要考虑对于区间 \([1,n]\) 内的所有数,一个数的贡献就是质因数的个数 \(+x-k+1\)。
如果从每个数开始进行质因数分解,时间复杂度是 \(O(n\sqrt{n})\),不能接受啊。
那么我们可以仿照埃筛的思想,对于每个质数,找到它的所有倍数,对那个数进行贡献,那思路就非常清晰了。
因为会有多个同样质因数的情况,所以需要对枚举的倍数进行拆分。
总时间复杂度约为 \(O(n\log n \log \log n)\)。
能过反正。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
const int N=1e7+7;
int ans[N];
bitset<N>vis;
void calc()
{for(int i=2;i<=n;i++){if(!vis[i]){for(int j=1;j*i<=n;j++){vis[j*i]=1;int k=j*i;while(k%i==0) ans[i]+=(n-(j*i)+1),k/=i;}}}
}
void write(int x)
{if(x>9) write(x/10);putchar(x%10+'0');
}
signed main()
{freopen("factorial.in","r",stdin);freopen("factorial.out","w",stdout);scanf("%lld",&n);calc();int flag=0;printf("f(%lld)=",n);for(int i=1;i<=n;i++){if(ans[i]==0) continue;if(flag)putchar('*');if(ans[i]==1) write(i);else write(i),putchar('^'),write(ans[i]);flag=1;}return 0;
}
博弈树
结论题。
如果在直径中点那么 \(Bob\) 获胜,否则 \(Alice\) 获胜。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int head[N],ecnt{};
struct EDGE{int nxt,to;
}e[N<<2];
inline void add(int u,int v)
{e[++ecnt].nxt=head[u];head[u]=ecnt;e[ecnt].to=v;
}
int f[N],d[N];
int maxval{},maxpos;
inline void dfs1(int u,int fa)
{f[u]=fa,d[u]=d[fa] + 1;if(d[u]>maxval) maxval=d[u],maxpos=u;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(v==fa) continue;dfs1(v,u);}
}
int dfn[N],cnt{};
inline void dfs2(int u,int fa)
{if(fa==0) maxval=fa;f[u]=fa,d[u]=d[fa]+1;dfn[u]=++cnt;if(d[u]>maxval) maxval=d[u],maxpos=u;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(v==fa) continue;dfs2(v,u);}
}
vector<int>zj;
bool fnd=0;
inline void dfs3(int u,int fa)
{if(u==maxpos){fnd=1;zj.push_back(u);return;}for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(v==fa) continue;dfs3(v,u);if(fnd) {zj.push_back(u);return;}}
}
signed main()
{freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);int n,q;cin>>n>>q;for(int i=1;i<n;i++){int u,v;cin>>u>>v; add(u,v),add(v,u);}dfs1(1,0);int rt=maxpos;dfs2(rt,0),dfs3(rt,0);int siz=zj.size();int pos=0;if(siz%2==1) pos=zj.at(siz>>1);while(q--){int u;cin>>u;if(pos==u) puts("Bob");else puts("Alice");}return 0;
}
划分
其实很简单。我们考虑分两种情况讨论。
第一种是题目给的特殊性质,满足 \(\forall i\in [1,k],a_i=0\)。这种情况就是前 \(m\) 个空,\(m\ge k\),能插最多 \(k\) 个板,那么划分成 \(k\) 段的方案数就是 \(\displaystyle\binom{m}{k-1}\)。
但是题目上说可以划分成大于等于 \(k\) 段,那总方案数就是 \(\displaystyle\sum_{i=k-1}^{m}\binom{m}{i}\)。
另一种情况,我们想到之前那个划分线段集合的题。那道题有一个贪心策略就是找 \(k-1\) 个单个的线段去确保答案最大。而这道题可以沿用这种策略。考虑选取一个长度为 \(n-k+1\) 的字典序最大的段,因为要保证最大的数的位数最多,这样才能使答案最大。其他 \(k-1\) 段就是单个的点。
然后,考虑到一个二进制数的最后一位可以更改,那么我们只需要使用哈希去匹配有多少长度为 \(n-k\) 的子串是和最大答案相匹配的,统计即为答案。
题目里有 hack 数据,你需要找一个较为优秀的模数去双哈希,要不然会有一个点卡住。
考虑有边界情况 \(n=k\),这时候答案是 \(\displaystyle \sum_{k=1}^n[a_i=1]\),方案数为 \(1\)。
顺便拿了最优解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
const int N=2e6+5;
char c[N];
const int mod=998244353;
int mx=0;
int ppow(int a,int b)
{int res=1;while(b){if(b&1) res=(res*a)%mod;a=(a*a)%mod,b>>=1;}return res;
}
int f[N],g[N];
void init()
{f[0]=g[0]=1;for(int i=1;i<=n;i++)f[i]=(f[i-1]*i)%mod;g[n]=ppow(f[n],mod-2);for(int i=n-1;i>=1;i--)g[i]=(g[i+1]%mod*(i+1)%mod);
}
int getc(int a,int b)
{return f[a]*g[a-b]%mod*g[b]%mod;
}
#define ull unsigned long long
ull p=13331,p1=1313131;
const ull md=20091119;
ull hs[N],pp[N];
ull hs1[N],pp1[N];
signed main()
{
// freopen("C-ex-2.in","r",stdin);freopen("divide.in","r",stdin);freopen("divide.out","w",stdout);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>k;cin>>(c+1);if(n==k){int ccnt=0;for(int i=1;i<=n;i++) ccnt+=(c[i]=='1');return cout<<ccnt<<" 1",0;}bool _=1;for(int i=1;i<=k;i++) _&=(c[i]=='0');if(_){int ans=0;for(int i=k+1;i<=n;i++) ans=((ans*2ll)%mod+c[i]-'0')%mod;init();int ans2=0,cnt=0;for(int i=1;i<=n;i++){if(c[i]=='1')break;++cnt;}if(cnt==n)--cnt;for(int i=k-1;i<=cnt;i++)ans2=(ans2+getc(cnt,i))%mod;cout<<ans%mod<<" "<<ans2%mod<<"\n";return 0;}int len=n-k+1;int mxpos=1;for(int i=1;i+len-1<=n;i++){int j=i+len-1;int now=mxpos-1;bool flag=0;for(int k=i;k<=j;k++){now++;if(c[k]==c[now])continue;if(c[k]>c[now]) {flag=1;break;}else break;}if(flag) mxpos=i;}
// cout<<mxpos;int ans1=0;for(int i=mxpos;i<=mxpos+len-1;i++) ans1=(ans1*2)%mod+c[i]-'0';for(int i=1;i<mxpos;i++) ans1=(ans1+c[i]-'0')%mod;for(int i=mxpos+len;i<=n;i++) ans1=(ans1+c[i]-'0')%mod;cout<<ans1<<" ";ull cmp=0,cmp1=0;pp[0]=1,pp1[0]=1;for(int i=1;i<=n;i++){hs[i]=hs[i-1]*p+(int)c[i];pp[i]=pp[i-1]*p;hs1[i]=hs1[i-1]*p1%md+(int)c[i];hs1[i]%=md;pp1[i]=pp1[i-1]*p1%md;}int ans2=0;for(int i=mxpos;i<mxpos+len-1;i++) cmp=cmp*p+(int)c[i];for(int i=mxpos;i<mxpos+len-1;i++) cmp1=cmp1*p1%md+(int)c[i],cmp1%=md;cmp1%=md;for(int i=1;i+len-1<=n;i++){ull now=hs[i+len-2]-hs[i-1]*pp[len-1];ull now2=((hs1[i+len-2]-hs1[i-1]*pp1[len-1]%md+md)%md+md)%md;if(now==cmp&&now2==cmp1)++ans2,ans2%=mod;}cout<<ans2;return 0;
}
/*
21 3
1001010101010111001111110 6
0000000000*/