暑期集训7
130 rank 39
T1:约瑟夫问题(见专题)
T2:暴力模拟
T3 让人痴迷(痴狂)的数论
T4:【甜圈】线段树(hash区间加乘或者直接维护区间信息)
T3:给你T组询问,每组询问(n,k)求n!从最低位开始非0的k位。(n<=1e100,k<=3)
就是要求n!/\(10^k\)(%1000),直接求出结果困难(??)考虑拆分成2个部分,n!/\(10^k\)(%125),n!/\(10^k\)(%8),然后用CRT进行合并。
但是其实不用CRT考虑n!中2的因数个数远>5,所以(n!)/\(10^k\)(%8)绝大部分是0【除了n<=6此时阶乘不满足<=8自然不能用后文的简单算法,需要直接暴算出来】,即存在x,是8的倍数,x%125=ans%125。
考虑求出ans。就是n!/\(10^k\)(%125)。对分母进行拆分,\(10^k=2^k*5^k\)其中\(n!/5^k\)(mod 125)可以用扩展卢卡斯求出。证明过程:https://oi-wiki.org/math/number-theory/lucas/
其实就是分成3个部分:
【1】递归子问题求解的$(n/5)! / 5^m \(【2】整块预处理的FAC【125】的次幂【3】散块预处理的答案fac.
细节就是\)fac[125]^(n/(5 ^ m))$在mod 125意义下指数需要mod上phi[125]=100,因为gcd(125,n/\(5^k\))=1.
最后就是把答案*上\(2^m\)(mod 125).同样【1】求\(2^m\),指数优化,\(2^m \equiv 2^{m mod phi(125)}\),一个求指数m的技巧,即求阶乘x!的因数p的个数=\(\sum_{i=1}^{\propto } x/p^{i}\)/
from GZK
char s[M];
int l,k;
int fac[M],FAC[M];
struct big{
int a[M],len;
void get(){
len=(l-1)/4+1;
for(int i=0;i<=100;i++) a[i]=0;
for(int i=1;i<len;i++){
a[i]=s[l-4*i]*1000+s[l-4*i+1]*100+s[l-4*i+2]*10+s[l-4*i+3];
}
for(int i=1;i<=(l-1)%4+1;i++){
a[len]=a[len]*10+s[i-1];
}
}
void operator /= (int k){
for(int i=len;i;i--){
a[i-1]+=a[i]%k*10000;
a[i]/=k;
}
while(a[len]==0&&len>0) len--;
}
int operator % (int k){
return a[1]%k;
}
bool operator > (int k){
return len>1||a[1]>k;
}
};
big n,m;
int qpow(int x,int k,int Mod){
int res=1;
while(k){
if(k&1) res=res*x%Mod;
x=x*x%Mod;
k>>=1;
}
return res;
}
int ext(big n){
if(n>125);
else return fac[n.a[1]];
int x=n%125;
n/=5;
int K=ext(n);
n/=25;
return K*qpow(FAC[125],n%100,125)%125*FAC[x]%125;
}
void output(int x,int k){//0 1 2 3
// for(int i=k;i;i--){
// if(x%Mod[i]/Mod[i-1]==0) printf("0");
// else break;
// }
if(k==1)printf("%01d\n",x%Mod[k]);
else if(k==2)printf("%02d\n",x%Mod[k]);
else if(k==3)printf("%03d\n",x%Mod[k]);
}
int ans,t,tot;
void work(){
scanf("%s%d",s,&k);
l=strlen(s);
for(int i=0;i<l;i++) s[i]-=48;
n.get();
ans=ext(n);
if(l==1&&n.a[1]<=6)
{
int a=1;
for(int i=1;i<=n.a[1];i++){
a*=i;
}
while(a%10==0) a/=10;
output(a,k);
return;
}
m=n;tot=0;
while(m.len) m/=5,(tot+=m%100)%=100;
ans*=qpow(63,tot,125);
ans%=125;
for(int i=0;i<1000;i+=8){
if(i%125==ans)
{output(i,k);break;}
}
}
signed main(){
fac[0]=FAC[0]=1;
for(int i=1;i<=125;i++){
int j=i;
while(j%5==0) j/=5;
fac[i]=fac[i-1]*j%125;
FAC[i]=FAC[i-1]*(i%5==0?1:i)%125;
}
int t=read();
while(t--){
work();
}
return 0;
}
tips:对于暴力,其实只要预处理出faci在除去末尾的0之后的%1e7之后的结果,在查询的时候输出,就可以得到50tps的好成绩,但是注意%1e7,就是模数尽量大一些,不然会有精度误差。(如果你%100,最后fac[1e7]的结果会面目全非......)
from Soytony
int t,n,k;
int fac[maxn];
int main(){
fac[1]=1;
ll now=1;
for(int i=2;i<=maxn-10;++i){
now=now*i;
while(now%10==0&&now) now/=10;
now%=1000000000;
fac[i]=now;
}
t=read();
while(t--){
int n=read(),k=read();
if(k==1) printf("%01d\n",fac[n]%10);
else if(k==2) printf("%02d\n",fac[n]%100);
else printf("%03d\n",fac[n]%1000);
}
return 0;
}
T4:给你n个盒子,初始为空,支持t个操作,每次(l,r,xi),表示在[l,r]区间编号的盒子有序放上xi编号物品。求最终满足盒子里的物品恰好是k个而且编号从小到大1--k的盒子数量。
[1]赛时自己打了个线段树,WA得很潇洒(10tps)
[2]正解1:线段树的思路,但是非常巧妙而且简单。你想象每次给一段区间操作,就像按照顺序放一堆有编号的东西,我只关注每次相邻的编号是否连续,因此我只在树上每个节点维护
2个信息:完整区间覆盖的最早的物品编号st,最晚的ed。如果不是完整继续下放,就像是对于包含关系的节点形成了由上到下的由st和ed拼接起来的“一条龙”。
(1)可以很好避免对于覆盖问题的判断,因为如果维护lazy标记那最新的lazy会覆盖上一个,但是我下放的时候节点合法性需要用到最早的一个
(2)可以及时排除冗余,当且仅当拼接会产生不合法,及时=-1,update和query根本不进去,复杂度就有了保证(本来就有保证!)
正解2:hash维护序列是否合法,区间加乘。
hash
const int N=2e5+10;
ull jia[N<<2],che[N<<2];
ull fina;
#define lson (rt<<1)
#define rson (rt<<1|1)
const ull base=131;
int n,k,t;
inline void build(int rt,int l,int r)
{
che[rt]=1;if(l==r)return ;
int mid=(l+r)>>1;
build(lson,l,mid);build(rson,mid+1,r);
}
inline void pushdown(int rt)
{
if(che[rt]!=1)//乘的在先
{
che[lson]*=che[rt];
che[rson]*=che[rt];
jia[lson]*=che[rt];
jia[rson]*=che[rt];
che[rt]=1;
}
if(jia[rt])
{
jia[lson]+=jia[rt];
jia[rson]+=jia[rt];
jia[rt]=0;
}
}
inline void update(int rt,int l,int r,int L,int R,int opt)
{
if(L<=l&&r<=R)
{
jia[rt]*=base;che[rt]*=base;jia[rt]+=opt;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)update(lson,l,mid,L,R,opt);
if(R>mid)update(rson,mid+1,r,L,R,opt);
}
inline int query(int rt,int l,int r)
{
if(l==r)
{
if(che[rt]+jia[rt]==fina)return 1;
return 0;
}
int mid=(l+r)>>1;
pushdown(rt);
return query(lson,l,mid)+query(rson,mid+1,r);
}
int main()
{
//freopen("d.in","r",stdin);
//freopen("c1.out","w",stdout);
n=re(),k=re(),t=re();fina=1;
_f(i,1,k)fina=fina*base+i;
build(1,1,n);
_f(i,1,t)
{
int lo=re(),ro=re(),xi=re();
update(1,1,n,lo,ro,xi);
}
chu("%d",query(1,1,n));
return 0;
}
const int N=2e5+10;
int n,k,t;
#define lson (rt<<1)
#define rson (rt<<1|1)
int st[N<<2],ed[N<<2];
inline void pushdown(int rt)
{
if(!st[rt])return;
if(!st[lson])st[lson]=st[rt],ed[lson]=ed[rt];
else if(ed[lson]!=st[rt]-1)ed[lson]=st[lson]=-1;
else ed[lson]=ed[rt];
if(!st[rson])st[rson]=st[rt],ed[rson]=ed[rt];
else if(ed[rson]!=st[rt]-1)ed[rson]=st[rson]=-1;
else ed[rson]=ed[rt];
st[rt]=ed[rt]=0;
}
inline void update(int rt,int l,int r,int L,int R,int opt)
{
if(st[rt]==-1)return;
if(L<=l&&r<=R)
{
if(!st[rt]){st[rt]=ed[rt]=opt;}
else if(ed[rt]!=opt-1)st[rt]=ed[rt]=-1;
else ed[rt]=opt;
return;
}
int mid=(l+r)>>1;pushdown(rt);
if(L<=mid)update(lson,l,mid,L,R,opt);
if(R>mid)update(rson,mid+1,r,L,R,opt);
}
inline int query(int rt,int l,int r)
{
if(st[rt]==-1)return 0;
if(l==r)return (st[rt]==1&&ed[rt]==k);
pushdown(rt);int mid=(l+r)>>1;
return query(lson,l,mid)+query(rson,mid+1,r);
}
int main()
{
//freopen("d.in","r",stdin);
//freopen("c1.out","w",stdout);
n=re(),k=re(),t=re();
_f(i,1,t)
{
int lk=re(),rk=re(),xi=re();
update(1,1,n,lk,rk,xi);
}
chu("%d",query(1,1,n));
return 0;
}
浙公网安备 33010602011771号