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\)

posted @ 2022-08-28 17:26  s1monG  阅读(43)  评论(0)    收藏  举报