22.8.28 总结
A
求最小 \(n\) 使得 \(n^2\) 为 \(k\) 的倍数且 \(n\) 不为 \(k\) 的倍数。
令 \(k=p_1^{q1}p_2^{q2}...\).
则使 \(n= p_1^{\left\lceil\dfrac{q1}{2}\right\rceil} p_2^{\left\lceil\dfrac{q2}{2}\right\rceil}\)...
code
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long int64;
const int64 N=1e6+10;
int64 k,s,f=1;
int main() {
scanf("%lld",&k); s=k;
for(int64 i=2; i<=N; i++) {
int p=0;
while(k%i==0) {p++; k/=i;}
f*=pow(i,(p+1)/2);
}
if(f%s!=0) printf("%lld\n",f);
else printf("-1\n");
return 0;
}
B
有一个 \(a\) 数列其中 \(1\le a_i \le 9\)。若相邻两个数的互质,则可以交换。
交换若干,问可以得到多少不同数列。
我们先不考虑 \(1,5,7\),因为它们可以插入到数列任意位置。
然后就剩下了 \(2,3,4,6,8,9\),
其中 \(6\) 是不能与其他数交换了,所以用 \(6\) 给数列分段。
每个数列里有 \(2,4,8\) 和 \(3,9\),分为两组,每组之间相对顺序不变。
设 \(2,4,8\) 有 \(p\) 个, \(3,9\) 有 \(q\) 个。
这组答案为 \(C_{p+q}^q\), 每组答案乘起来即可。
最后考虑 \(1,5,7\),设它们出现次数为 \(x,y,z\).
相似的,答案乘上 \(C_n^x\times C_{n-x}^y \times C_{n-x-y}^z\).
code
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long int64;
const int64 N=1e5+10,mod=998244353;
int64 n,a[N],frac[N],cnt[10],p,q,ans=1;
int64 exgcd(int64 a,int64 b,int64 &x,int64 &y) {
if(b==0) {x=1; y=0; return a;}
int64 g=exgcd(b,a%b,x,y),z=x;
x=y; y=z-(a/b)*y;
return g;
}
int64 C(int64 n,int64 m) {
if(n==0||m==0) return 1;
int64 res=frac[n];
int64 div1,div2,y;
exgcd(frac[m],mod,div1,y); div1=(div1%mod+mod)%mod;
exgcd(frac[n-m],mod,div2,y); div2=(div2%mod+mod)%mod;
res=res*div1%mod;
res=res*div2%mod;
return res;
}
signed main() {
scanf("%lld",&n);
frac[0]=1;
for(int64 i=1; i<=n; i++) frac[i]=frac[i-1]*i%mod;
for(int64 i=1; i<=n+1; i++) {
if(i<=n) scanf("%lld",&a[i]);
if(a[i]==6||i==n+1) {
ans=ans*C(p+q,p)%mod;
p=q=0;
}
else if(a[i]%2==0) p++;
else if(a[i]%3==0) q++;
cnt[a[i]]++;
}
ans=ans*C(n,cnt[1])%mod;
ans=ans*C(n-cnt[1],cnt[5])%mod;
ans=ans*C(n-cnt[1]-cnt[5],cnt[7])%mod;
printf("%lld\n",ans);
return 0;
}
C
有一个 \(a\) 数列,现在要想让前 \(i\) 个数相等。
你可以让一个数 \(+1\) 花费代价 \(p\), \(-1\) 花费代价 \(q\).
可以证明,最后所有数相等与其中的一个数。
还可以证明,按照大小排序,每一次决策变化只会移动一个数。
第一次决策为第一个数。
code
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<set>
using namespace std;
typedef long long int64;
const int64 N=1e5+10,inf=1e16;
int64 n,f,g,a[N],b[N],v[N];
int64 c1[N],c2[N],lastans=-1;
set<int64> s;
void modify(int64 c[],int64 p,int64 x) {
for(; p<N; p+=p&-p) c[p]+=x;
}
int64 query(int64 c[],int64 p) {
int64 res=0;
for(; p; p-=p&-p) res+=c[p];
return res;
}
int64 calc(int64 p) {
int64 res=0;
int64 num0=query(c2,n),num=query(c2,p);
int64 sum0=query(c1,n),sum=query(c1,p);
res+=f*(num*b[p]-sum);
res+=g*(sum0-sum-(num0-num)*b[p]);
return res;
}
int64 solve() {
if(lastans==-1) {
lastans=*s.begin();
return 0;
}
set<int64> ::iterator it1,it2,it3;
int64 ans1=inf,ans2=inf,ans3=inf,ans0;
it1=it2=it3=s.lower_bound(lastans);
ans1=calc(*it1);
if(it2!=s.begin()) it2--;
ans2=calc(*it2);
if(it3!=--s.end()) it3++;
ans3=calc(*it3);
ans0=min(ans1,min(ans2,ans3));
if(ans0==ans1) {
lastans=*it1;
} else if(ans0==ans2) {
lastans=*it2;
} else lastans=*it3;
return ans0;
}
signed main() {
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
scanf("%lld%lld%lld",&n,&f,&g);
for(int64 i=1; i<=n; i++) {
scanf("%lld",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
for(int64 i=1; i<=n; i++)
a[i]=lower_bound(b+1,b+n+1,a[i])-b;
for(int64 i=1; i<=n; i++) {
s.insert(a[i]);
modify(c1,a[i],b[a[i]]);
modify(c2,a[i],1);
printf("%lld\n",solve());
}
return 0;
}
D
在一棵树上任意简单路径的权值 \(2^d\) , \(d\) 为长度。
\(q\) 次询问,每次询问经过 \(x,y\) 的所有路径权值和。
考虑把一条路径对答案的贡献分成三部分,\(x\) 的子树内(\(y\) 为根时),\(y\) 的子树内(\(x\) 为根
时),\(x\) 和 \(y\) 之间的路径。那么相同部分可以相加,最后乘起来就行了。对于前两部分可以
换根或者预处理讨论,第三部分需要快速的求 \(Lca\)。

浙公网安备 33010602011771号