A
构造的试卷每一题都是 A 0 0 0 0
,这样方案数为 3 n − 1 × 4 3^{n-1}\times 4 3n−1×4。
证明的话参考官方题解吧,我这只是手玩出来之后觉得大概没有更优解于是就写了。
代码大概没人需要吧……还是要稍微放一下的
#include <cstdio>
int n;
int main()
{
scanf("%d",&n);
int ans=4;for(int i=2;i<=n;i++)ans=3ll*ans%998244353;
printf("%d\n",ans);
for(int i=1;i<=n;i++)printf("A 0 0 0 0\n");
}
B
不难发现肯定要先按权值将所有修路操作排序,然后每次操作要将尽可能多的连通块连在一起。
考虑一次修路,假设限制个数为 p i p_i pi,若 d i s ( u i , v i ) > p i dis(u_i,v_i)>p_i dis(ui,vi)>pi,那么这路上所有连通块都必定可以连在一起,用一个并查集来维护自己所在连通块中深度最小的节点即可,每次暴力往上跳。这部分的总复杂度为 O ( n log n ) O(n\log n) O(nlogn)。(注意这里是不能按秩合并的,所以不是 O ( n α n ) O(n\alpha_n) O(nαn)。)
当 d i s ( u i , v i ) ≤ p i dis(u_i,v_i)\leq p_i dis(ui,vi)≤pi 时,考虑将所有限制连边,然后找到度数最小的点 x x x,将不跟他相连的点与他合并,这部分要用另外一个并查集来维护,并且上面那个部分合并连通块时,也要顺便维护这个并查集。
然后再暴力枚举与 x x x 相连的点,将他们之间进行合并,由于 x x x 选取了度数最小的点,所以这部分复杂度不超过 O ( p ) O(p) O(p)。
最后再看看与 x x x 相连的点是否能跟不与 x x x 相连的点进行合并,这个也可以在 O ( p ) O(p) O(p) 内判断。
于是时间复杂度大概就是 O ( ( n + p ) log n ) O((n+p)\log n) O((n+p)logn),代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 300010
#define pb push_back
int n,m,k,fa[maxn],dep[maxn];
struct edge{
int x,y,z,t;}d[maxn];
bool cmp(edge x,edge y){
return x.z<y.z;}
vector<edge> con[maxn];
bool check(int x,int y,int p){
while(p--){
if(dep[x]>dep[y])swap(x,y);
y=fa[y];if(x==y)return false;
}
return true;
}
struct dsu{
int fa[maxn];
int findfa(int x){
return x==fa[x]?x:fa[x]=findfa(fa[x]);}
dsu(){
for(int i=1;i<=maxn-10;i++)fa[i]=i;}
}A,B;
long long ans=0;
void link(int x,int y,int z){
int px=x,py=y;
x=B.findfa(x);y=B.findfa(y);
if(x!=y)B.fa[y]=x,ans+=z;//,printf("link : %d %d %d\n",px,py,z);
}
vector<int> e[maxn];
edge sta[maxn];int top=0;
void add_edge(int x,int y){
e[x].pb(y);e[y].pb(x);sta[++top]=(edge){
x,y,0,0};}
void clear_edge(int x=0,int y=0){
while(top)x=sta[top].x,y=sta[top--].y,e[x].clear(),e[y].clear();}
int S[maxn],sn;
void get_chain(int x,int y){
sn=0;
bool stop;do{
stop=(x==y);
if(dep[x]>dep[y])swap(x,y);
S[++sn]=y;y=fa[y];
}while(!stop);
}
bool v[maxn];
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i=2;i<=n;i++)scanf("%d",&fa[i]),dep[i]=dep[fa[i]]+1;
for(int i=1;i<=m;i++)scanf("%d %d %d",&d[i].x,&d[i].y,&d[i].z),d[i].t=i;
for(int i=1,x,y,t;i<=k;i++){
scanf("%d %d %d",&t,&x,&y);
con[t].pb((edge){
x,y,0,0});
}
sort(d+1,d+m+1,cmp);
for(int i=1;i<=m;i++){
int x=d[i].x,y=d[i].y,z=d[i].z,t=d[i].t;
if(check(x,y,con[t].size())){
x=A.findfa(x);y=A.findfa(y);
while(x!=y){
if(dep[x]>dep[y])swap(x,y);
link(y,fa[y],z);A.fa[y]=fa[y];
y=A.findfa(y);
}
}else{
for(edge i:con[t])add_edge(i.x,i.y);
get_chain(x,y);int mi=S[1];
for(int j=2;j<=sn;j++)if(e[S[j]].size()<e[mi].size())mi=S[j];
for(int j:e[mi])v[j]=true;
for(int j=1;j<=sn;j++)if(!v[S[j]])link(mi,S[j],z);
for(int j:e[mi]){
int tot=0;for(int p:e[j])tot+=v[p];
if(e[j].size()-tot-1<sn-e[mi].size()-1)link(j,mi,z);
}
for(int j:e[mi])v[j]=false;
for(int j:e[mi]){
for(int p:e[j])v[p]=true;
for(int p:e[mi])if(!v[p])link(j,p,z);
for(int p:e[j])v[p]=false;
}
clear_edge();
}
}
printf("%lld",ans);
}
C
膜一膜神仙就会了。
如果你对下面的莫反有什么疑问的话,可以看看这个。
首先大力推推柿子,显然有: l c m ( i , j ) = i j gcd ( i , j ) lcm(i,j)=\frac {ij} {\gcd(i,j)} lcm(i,j)=gcd(i,j)ij,代入得:
∑ j = 1 n i d × j d × gcd ( i , j ) c − d × x j ≡ b i \sum_{j=1}^n i^d \times j^d \times \gcd(i,j)^{c-d} \times x_j \equiv b_i j=1∑nid×jd×gcd(i,j)c−d×xj≡bi
神仙告诉我们,其实长这样的都可以做:
∑ j = 1 n g ( i ) × h ( j ) × f ( gcd ( i , j ) ) × x j ≡ b i \sum_{j=1}^n g(i) \times h(j) \times f(\gcd(i,j)) \times x_j \equiv b_i j=1∑ng(i)×h(j)×f(gcd(i,j))×xj≡bi
设 f ( n ) = ∑ k ∣ n f ′ ( k ) f(n)=\sum_{k|n}f'(k) f(n)=∑k∣nf′(k),由于我们知道 f f f,所以可以大力反演一波得到 f ′ f' f′,代入得:
∑ j = 1 n ∑ k ∣ gcd ( i , j ) g ( i ) × h ( j ) × f ′ ( k ) × x j ≡ b i ∑ k ∣ i f ′ ( k ) ∑ k ∣ j h ( j ) × x j ≡ b i g ( i ) \begin{aligned} \sum_{j=1}^n \sum_{k|\gcd(i,j)} g(i) \times h(j) \times f'(k) \times x_j &\equiv b_i\\ \sum_{k|i} f'(k) \sum_{k|j} h(j) \times x_j &\equiv \frac {b_i} {g(i)}\\ \end{aligned} j=1∑nk∣gcd(i,j)∑g(i)×h(j)×f′(k)×xjk∣i∑f′(k)k∣j∑h(j)×xj≡bi≡g(i)bi
设 t ( n ) = ∑ n ∣ i h ( i ) × x i t(n)=\sum_{n|i} h(i)\times x_i t(n)=∑n∣ih(i)×xi,代入有:
∑ k ∣ i f ′ ( k ) t ( k ) ≡ b i g ( i ) \sum_{k|i} f'(k) t(k) \equiv \frac {b_i} {g(i)} k∣i∑f′(k)t(k)≡g(i)bi
可能这样更顺眼?
b i g ( i ) ≡ ∑ k ∣ i f ′ ( k ) t ( k ) \frac {b_i} {g(i)} \equiv \sum_{k|i} f'(k) t(k) g(i)bi≡k∣i∑f′(k)t(k)
由于我们知道 b i g ( i ) \frac {b_i} {g(i)} g(i)bi,所以再次反演可以求出 f ′ ( k ) t ( k ) f'(k)t(k) f′(k)t(k),除掉 f ′ ( k ) f'(k) f′(k) 就得到了 t ( k ) t(k) t(k)。
然而 t ( k ) t(k) t(k) 也是个莫反的形式,所以最后莫反一波求出 h ( i ) × x i h(i)\times x_i h(i)×xi,然后除掉 h ( i ) h(i) h(i) 就求出 x i x_i xi 了。
由于在这题中 g ( i ) = h ( i ) g(i)=h(i) g(i)=h(i),所以代码里面统一用 g ( i ) g(i) g(i) 了,以及代码里面的 f f f 其实就是柿子里的 f ′ f' f′,柿子里的 f f f 其实代码里不需要存下来。
h ( i ) × x i ≡ ∑ i ∣ n μ ( n i ) t ( n ) h(i)\times x_i \equiv \sum_{i|n} \mu(\frac n i)t(n) h(i)×xi≡i∣n∑μ(in)t(n)
代码如下:
#include <cstdio>
#define mod 998244353
#define ll long long
#define maxn 300010
int n,c,d,q;
int mu[maxn],prime[maxn],tot=0;
bool v[maxn];
ll ksm(ll x,int y)
{
ll re=1;
y=(y%(mod-1)+mod-1)%(mod-1);//如果是负数那么就加上mod-1
while(y)
{
if(y&1)re=re*x%mod;
x=x*x%mod;y>>=1;
}
return re;
}
#define inv(x) ksm(x,-1)
ll b[maxn],g[maxn],f[maxn],t[maxn];
void work()
{
mu[1]=1;
for(int i=2;i<=n;i++)//这里真的难受……我以为是莫反打错了调了一个小时,结果是线性筛错了QAQ
{
if(!v[i])prime[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*prime[j]<=maxn-10;j++)
{
v[i*prime[j]]=true;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)g[i]=ksm(i,d);//求g
for(int i=1;i<=n;i++)if(mu[i]!=0)//求f'
for(int j=1;i*j<=n;j++)
f[i*j]=(f[i*j]+(mod+mu[i]*ksm(j,c-d))%mod)%mod;
}
int main()
{
scanf("%d %d %d %d",&n,&c,&d,&q);
work();
while(q--)
{
for(int i=1;i<=n;i++)scanf("%lld",&b[i]),b[i]=b[i]*inv(g[i])%mod;//求出b[i]/g[i]
for(int i=1;i<=n;i++)t[i]=0;
for(int i=1;i<=n;i++)if(mu[i]!=0)//求出t[i]
for(int j=1;i*j<=n;j++)
t[i*j]=(t[i*j]+(mod+mu[i]*b[j])%mod)%mod;
bool v=false;
for(int i=1;i<=n;i++)
if(!f[i]&&t[i]){
v=true;break;}
else t[i]=t[i]*inv(f[i])%mod;
if(v){
printf("-1\n");continue;}
for(int i=1;i<=n;i++)b[i]=0;//求出答案,由于此时b数组没用了,我们理所当然地把他拿过来打打工
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
b[i]=(b[i]+(mod+mu[j/i]*t[j])%mod)%mod;
for(int i=1;i<=n;i++)
printf("%lld ",b[i]*inv(g[i])%mod);
printf("\n");
}
}