190921CSP-S模拟题解

T1:lcm
求给定的数集的任意非空子集的lcm之和
数集大小2000,元素大小200

要求lcm,那显然唯一分解
然后很容易发现200以内的所有素数中,在某个合数的分解中对应幂次大于1的只有 200 \sqrt{200} 200 级别,即 2 , 3 , 5 , 7 , 11 , 13 2,3,5,7,11,13 2,3,5,7,11,13
所以我们可以把这些素数出现的状态压缩一下,然后每个合数除掉这些素数之后剩下的一定只剩下一个素数或者1,那就可以按这个素数分类然后dp就完了

Code:

#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define db double
#define se second
#define fi first
#define mod 1000000007
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=2005;
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void Mul(int &x,int y){x=mul(x,y);}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
int n,a[N];
int pri[41]={17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,1};
int f[8][5][4][3][3][3],g[8][5][4][3][3][3],sum[8][5][4][3][3][3];
vector<int>fac[41];
int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) for(int j=0;j<=41;j++) if(a[i]%pri[j]==0) {a[i]/=pri[j],fac[j].pb(a[i]);break;}
	f[0][0][0][0][0][0]=1;
	int ans=0;
	for(int i=40;~i;i--){
		if(!fac[i].size()) continue;
		memset(sum,0,sizeof(sum));
		for(int j=0;j<fac[i].size();j++){
			memset(g,0,sizeof(g));
			int ml=1,n1=0,n2=0,n3=0,n4=0,n5=0,n6=0;
			int v=fac[i][j];
			while(v%2==0) v/=2,++n1;while(v%3==0) v/=3,++n2;while(v%5==0) v/=5,++n3;while(v%7==0) v/=7,++n4;while(v%11==0) v/=11,++n5;while(v%13==0) v/=13,++n6;
			for(int s=1;s<=n1;s++) ml*=2;
			for(int a1=0;a1<8;a1++){
				for(int s=1;s<=n2;s++)ml*=3;
				
				for(int a2=0;a2<5;a2++){
					for(int s=1;s<=n3;s++)ml*=5;
					
					for(int a3=0;a3<4;a3++){
						
						for(int s=1;s<=n4;s++)ml*=7;
						
						for(int a4=0;a4<3;a4++){
							
							for(int s=1;s<=n5;s++)ml*=11;
							
							for(int a5=0;a5<3;a5++){
								
								for(int s=1;s<=n6;s++)ml*=13;
								
								for(int a6=0;a6<3;a6++){
									int mx1=max(n1,a1),mx2=max(n2,a2),mx3=max(n3,a3),mx4=max(n4,a4),mx5=max(n5,a5),mx6=max(n6,a6);
									inc(g[mx1][mx2][mx3][mx4][mx5][mx6],mul(f[a1][a2][a3][a4][a5][a6],mul(ml,pri[i])));
									inc(g[mx1][mx2][mx3][mx4][mx5][mx6],mul(sum[a1][a2][a3][a4][a5][a6],ml));
									if(a6<n6) ml/=13;
								}
								if(a5<n5) ml/=11;
							}
							if(a4<n4) ml/=7;
						}
						if(a3<n3) ml/=5;
					}
					if(a2<n2) ml/=3;
				}
				if(a1<n1) ml/=2;
			}
			for(int a1=0;a1<8;a1++)
				for(int a2=0;a2<5;a2++)
					for(int a3=0;a3<4;a3++)
						for(int a4=0;a4<3;a4++)
							for(int a5=0;a5<3;a5++)
								for(int a6=0;a6<3;a6++) inc(sum[a1][a2][a3][a4][a5][a6],g[a1][a2][a3][a4][a5][a6]);
		}
		for(int a1=0;a1<8;a1++)
			for(int a2=0;a2<5;a2++)
				for(int a3=0;a3<4;a3++)
					for(int a4=0;a4<3;a4++)
						for(int a5=0;a5<3;a5++)
							for(int a6=0;a6<3;a6++) inc(ans,sum[a1][a2][a3][a4][a5][a6]),inc(f[a1][a2][a3][a4][a5][a6],sum[a1][a2][a3][a4][a5][a6]);
	}
	cout<<ans;
	return 0;
}

T2:Xor
严重吐槽题面
“割”这一定义在网络流中指的是一个边集,这里却指点集,要不你就换个词描述非得用“
割“吗

题意:一张无向图,每个点有点权,每条边有边权,对于一个点的集合,若一条边的两个端点分别在集合内和集合的补集内,则这个集合的权值会异或上这条边的边权
定义一个点集为合法的当且仅当其任意非空子集的权值不为0,点集的价值为点集内的点的点权之和减去点集外的点的点权之和,请求出价值最大的点集的价值

首先异或有一个性质,一个数被相同的数异或两次其值不变,所以对于任意一个点,我们定义它的权值为连接它的所有边的权值的异或和,则一个点集的权值即为点集内的所有点的权值异或和,那么要求选一个点集,其任意子集的权值不为0即是要求里面所有点线性无关,那么按照点权排序做线性基即可

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
	ll res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e5+5;
struct info{
	ll key,val;
	info(){}
	info(ll _key,ll _val):key(_key),val(_val){}
	inline bool operator < (const info &b){return val>b.val;}
}p[N];
ll sset[N];
int main(){
	int n=read(),m=read();ll sum=0;
	for(int i=1;i<=n;i++) p[i].val=read(),sum+=p[i].val;
	for(int i=1;i<=m;i++){
		int x=read(),y=read();ll z=read();
		p[x].key^=z;p[y].key^=z;
	}
	sort(p+1,p+n+1);
	ll ans=0;
	for(int i=1;i<=n;i++){
		int f=0;
		for(int j=63;~j;j--){
			if(!p[i].key) break;
			if(!(p[i].key>>j)) continue;
			if(!sset[j]) {sset[j]=p[i].key,ans+=p[i].val;f=1;break;}
			else p[i].key^=sset[j];
		}
		if(!f) ans-=p[i].val;
	}
	cout<<ans;
	return 0;
}

T3:咕咕咕

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值