放了各种杂题。
2022.6.15
NOI2014 随机数生成器
先模拟,然后做个普及组贪心,维护每行能选的左右端点。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<18],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<18,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int x0,A,B,C,D;
int rnd[25000005],seq[25000005],L[5005],R[5005];
int n,m,q;
int main(){
x0=read(),A=read(),B=read(),C=read(),D=read();
rnd[0]=x0;
n=read(),m=read(),q=read();
for(int i=1;i<=n*m;++i)
{
LL t=LL(A)*rnd[i-1]*rnd[i-1]+LL(B)*rnd[i-1]+C;
rnd[i]=t%D;
}
for(int i=1;i<=n*m;++i) seq[i]=i;
for(int i=1;i<=n*m;++i) swap(seq[i],seq[rnd[i]%i+1]);
for(int i=1;i<=q;++i)
{
int u=read(),v=read();
swap(seq[u],seq[v]);
}
#define P rnd
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) P[seq[(i-1)*m+j]]=(i-1)*m+j;
for(int i=1;i<=n;++i) L[i]=1,R[i]=m;
for(int i=1;i<=n*m;++i)
{
int p=P[i];
int x=(p-1)/m+1,y=p-(x-1)*m;
if(L[x]<=y && y<=R[x])
{
write(i),putchar(' ');
for(int j=1;j<=n;++j)
{
if(j==x) continue;
if(j<x) R[j]=min(R[j],y);
else L[j]=max(L[j],y);
}
}
}
return 0;
}
CF923E Perpetual Subtraction
矩阵对角化板子题。
首先你可以写成一个线性变换的形式(虽然我又没看出来,菜了)。然后因为要求 \(m\) 次方所以考虑对角化。注意到这个矩阵的特征值很好找,特征矩阵很有特色(可以通过手算找规律,容易发现是一个组合数加上三角矩阵的形式,带一定的 \(-1\) 系数),这样就最终可以写成两个减法卷积的形式。
注意 \(E\) 和 \(E^{-1}\) 的运算顺序。
/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
const int MOD=998244353;
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
int readMod()
{
LL x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x%(MOD-1);
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline int Add(int u,int v){return u+v>=MOD?u+v-MOD:u+v;}
inline int Sub(int u,int v){return u-v>=0?u-v:u-v+MOD;}
inline int Mul(int u,int v){return LL(u)*LL(v)%MOD;}
int QuickPow(int x,int p)
{
if(p<0) p+=MOD-1;
int ans=1,base=x;
while(p)
{
if(p&1) ans=Mul(ans,base);
base=Mul(base,base);
p>>=1;
}
return ans;
}
typedef vector<int> Poly;
#define len(x) (int(x.size()))
const int GG=3,Gi=(MOD+1)/GG;
int rev[400005];
int fac[400005],ifac[400005];
inline int inv(int x){return x==0?0:Mul(ifac[x],fac[x-1]);}
void init(int up)
{
fac[0]=1;
for(int i=1;i<=up;++i) fac[i]=Mul(fac[i-1],i);
ifac[up]=QuickPow(fac[up],MOD-2);
for(int i=up-1;~i;--i) ifac[i]=Mul(ifac[i+1],i+1);
}
inline void makeRev(int lim){for(int i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)*(lim>>1));}
void NTT(Poly &r,int flag)
{
for(int i=0;i<len(r);++i) if(i<rev[i]) swap(r[i],r[rev[i]]);
for(int p=2;p<=len(r);p<<=1)
{
int len=p>>1;
int w=QuickPow(flag?GG:Gi,(MOD-1)/p);
for(int k=0;k<len(r);k+=p)
{
int cn=1;
for(int i=k;i<k+len;++i)
{
int dif=Mul(r[i+len],cn);
r[i+len]=Sub(r[i],dif);
r[i]=Add(r[i],dif);
cn=Mul(cn,w);
}
}
}
if(flag==0)
{
int inv=QuickPow(len(r),MOD-2);
for(int i=0;i<len(r);++i) r[i]=Mul(r[i],inv);
}
}
Poly operator + (Poly F,int v){F[0]=Add(F[0],v);return F;}
Poly operator + (int v,Poly F){F[0]=Add(F[0],v);return F;}
Poly operator - (Poly F,int v){F[0]=Sub(F[0],v);return F;}
Poly operator - (int v,Poly F){F[0]=Sub(F[0],v);return F;}
Poly operator * (Poly F,int v){for(int i=0;i<len(F);++i) F[i]=Mul(F[i],v);return F;}
Poly operator * (int v,Poly F){for(int i=0;i<len(F);++i) F[i]=Mul(F[i],v);return F;}
Poly operator + (Poly F,Poly G)
{
Poly ret;
int len=max(len(F),len(G));
F.resize(len),G.resize(len),ret.resize(len);
for(int i=0;i<len;++i) ret[i]=Add(F[i],G[i]);
return ret;
}
Poly operator - (Poly F,Poly G)
{
Poly ret;
int len=max(len(F),len(G));
F.resize(len),G.resize(len),ret.resize(len);
for(int i=0;i<len;++i) ret[i]=Sub(F[i],G[i]);
return ret;
}
Poly operator * (Poly F,Poly G)
{
Poly ret;
int len=len(F)+len(G)-1,lim=1;
while(lim<=len) lim<<=1;
F.resize(lim),G.resize(lim),ret.resize(lim);
makeRev(lim);
NTT(F,1),NTT(G,1);
for(int i=0;i<lim;++i) ret[i]=Mul(F[i],G[i]);
NTT(ret,0);
ret.resize(len);
return ret;
}
int main(){
init(400000);
Poly F,G;
int n=read(),m=readMod();
F.resize(++n);
for(int i=0;i<n;++i) F[i]=Mul(fac[i],read());
G.resize(n);
for(int i=0;i<n;++i) G[i]=ifac[i];
reverse(F.begin(),F.end());
F=F*G,F.resize(n);
reverse(F.begin(),F.end());
for(int i=0;i<n;++i) F[i]=Mul(F[i],QuickPow(inv(i+1),m)),F[i]=(i&1)?(MOD-F[i])%MOD:F[i];
reverse(F.begin(),F.end());
F=F*G,F.resize(n);
reverse(F.begin(),F.end());
for(int i=0;i<n;++i) F[i]=Mul(F[i],ifac[i]),F[i]=(i&1)?(MOD-F[i])%MOD:F[i],write(F[i]),putchar(' ');
return 0;
}
CF Gym 103415K Nagus Night
这个题竟然没自己做出来,我真的,。
首先算全集,减去 \(\gcd\) 不满足限制的,和 \(\gcd\) 满足限制 \(\operatorname{lcm}\) 不满足限制的。
前面两个都很简单就略过,主要是第三个。记 \(f(i,j)\) 为 \(\gcd(S) = i,\operatorname{lcm} = j\) 的贡献,容易发现这个可以直接 \(O(m \ln m)\) 枚举。并且发现 \(f(i,j) = i^n f(1, \frac{j}{i})\)。显然如果满足 \(f\) 的限制那么 \(S\) 也满足限制。那么拆开质因数贡献,里面也是类似于容斥的形式(全集,减去没有 \(0\) 的,减去没有 \(x_p\) 的,加上两个都没有的)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<18],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<18,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int MOD=998244353;
inline int Add(int x,int y){return x+y>=MOD?x+y-MOD:x+y;}
inline int Sub(int x,int y){return x<y?x-y+MOD:x-y;}
inline int Mul(int x,int y){return 1ll*x*y%MOD;}
int QuickPow(int x,int p)
{
int ans=1,base=x;
while(p)
{
if(p&1) ans=Mul(ans,base);
base=Mul(base,base);
p>>=1;
}
return ans;
}
int n,m,p,q;
int SolveA();
int SolveB();
int SolveC();
int main(){
n=read(),m=read(),p=read(),q=read();
write(Sub(SolveA(),Add(SolveB(),SolveC())));
return 0;
}
int SolveA(){return QuickPow(Mul(Mul(m,m+1),(MOD+1)/2),n);}
int SolveB()
{
static int f[200005];
for(int i=1;i<=m;++i) f[i]=Mul(QuickPow(i,n),QuickPow(Mul(Mul(m/i,m/i+1),(MOD+1)/2),n));
for(int i=m;i>=1;--i) for(int j=2*i;j<=m;j+=i) f[i]=Sub(f[i],f[j]);
int ret=0;
for(int i=q+1;i<=m;++i) ret=Add(ret,f[i]);
return ret;
}
bool vis[200005];
int prime[200005],cnt;
int mpr[200005];
void hisa(int up)
{
mpr[1]=1;
for(int i=2;i<=up;++i) mpr[i]=up;
for(int i=2;i<=up;++i)
{
if(!vis[i]) prime[++cnt]=i,mpr[i]=i;
for(int j=1;j<=cnt && prime[j]*i<=up;++j)
{
vis[prime[j]*i]=true;
mpr[prime[j]*i]=min(mpr[prime[j]*i],prime[j]);
}
}
}
int SolveC()
{
static int f[200005];
hisa(m);
for(int i=1;i<=m;++i)
{
int x=i;
f[i]=1;
while(x^1)
{
int t=x,P=mpr[x],tot=0;
while(mpr[t]==P) ++tot,t/=P;
x=t;
int a=0,b=0,c=0,d=0;
for(int j=0;j<tot;++j) c=Add(c,QuickPow(P,j));
a=Add(c,QuickPow(P,tot));
b=a-1,d=c-1;
a=QuickPow(a,n);
b=QuickPow(b,n);
c=QuickPow(c,n);
d=QuickPow(d,n);
f[i]=Mul(f[i],Sub(Add(a,d),Add(b,c)));
}
}
int ret=0;
for(int i=1;i<=q;++i) for(int j=i;j<p;j+=i) ret=Add(ret,Mul(QuickPow(i,n),f[j/i]));
return ret;
}
CF Gym 103415J Cafeteria
不写成矩阵转移形式,我真的,。我都不好说是我变蠢了还是变懒了。
容易写成矩阵转移形式,变成前缀逆和前缀积。容易发现铁 T 无疑。
考虑维护两个矩阵的左右乘,这个分析一下能出来怎么转移的。
最后回答询问的时候我们只需要求出一行一列就好了,做向量乘。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<18],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<18,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int MOD=998244353;
inline int Add(int x,int y){return x+y>=MOD?x+y-MOD:x+y;}
inline int Sub(int x,int y){return x<y?x-y+MOD:x-y;}
inline int Mul(int x,int y){return 1ll*x*y%MOD;}
int QuickPow(int x,int p)
{
int ans=1,base=x;
while(p)
{
if(p&1) ans=Mul(ans,base);
base=Mul(base,base);
p>>=1;
}
return ans;
}
int n,m,p,q;
int SolveA();
int SolveB();
int SolveC();
int main(){
n=read(),m=read(),p=read(),q=read();
write(Sub(SolveA(),Add(SolveB(),SolveC())));
return 0;
}
int SolveA(){return QuickPow(Mul(Mul(m,m+1),(MOD+1)/2),n);}
int SolveB()
{
static int f[200005];
for(int i=1;i<=m;++i) f[i]=Mul(QuickPow(i,n),QuickPow(Mul(Mul(m/i,m/i+1),(MOD+1)/2),n));
for(int i=m;i>=1;--i) for(int j=2*i;j<=m;j+=i) f[i]=Sub(f[i],f[j]);
int ret=0;
for(int i=q+1;i<=m;++i) ret=Add(ret,f[i]);
return ret;
}
bool vis[200005];
int prime[200005],cnt;
int mpr[200005];
void hisa(int up)
{
mpr[1]=1;
for(int i=2;i<=up;++i) mpr[i]=up;
for(int i=2;i<=up;++i)
{
if(!vis[i]) prime[++cnt]=i,mpr[i]=i;
for(int j=1;j<=cnt && prime[j]*i<=up;++j)
{
vis[prime[j]*i]=true;
mpr[prime[j]*i]=min(mpr[prime[j]*i],prime[j]);
}
}
}
int SolveC()
{
static int f[200005];
hisa(m);
for(int i=1;i<=m;++i)
{
int x=i;
f[i]=1;
while(x^1)
{
int t=x,P=mpr[x],tot=0;
while(mpr[t]==P) ++tot,t/=P;
x=t;
int a=0,b=0,c=0,d=0;
for(int j=0;j<tot;++j) c=Add(c,QuickPow(P,j));
a=Add(c,QuickPow(P,tot));
b=a-1,d=c-1;
a=QuickPow(a,n);
b=QuickPow(b,n);
c=QuickPow(c,n);
d=QuickPow(d,n);
f[i]=Mul(f[i],Sub(Add(a,d),Add(b,c)));
}
}
int ret=0;
for(int i=1;i<=q;++i) for(int j=i;j<p;j+=i) ret=Add(ret,Mul(QuickPow(i,n),f[j/i]));
return ret;
}