【8.11测试爆零记录】

第一题(diyiti)

直接Pollard-Rho分解完了上个容斥,发现有两种情况可以合并,xjbdfs一下就行了

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

using std::cerr;
using std::cout;

namespace Sieves{
	inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
	inline ll mul(ll a,ll b,ll mod){
		ll res=0;if(b>a)std::swap(a,b);
		for(;b;b>>=1,a=a+a>=mod?a+a-mod:a+a)(b&1)&&(res=res+a>=mod?res+a-mod:res+a);
		return res;
	}
	
	inline ll power(ll a,ll b,ll mod){
		ll res=1;a%=mod;
		for(;b;b>>=1,a=mul(a,a,mod))(b&1)&&(res=mul(res,a,mod));
		return res;
	}
	
	inline bool isprime(ll x){
		static cs int p[17]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};
		for(int re i=0;i<17;++i)if(x%p[i]==0)return x==p[i];
		if(p[16]>x)return false;
		ll t=x-1,s=0;
		while(!(t&1))t>>=1,++s;
		for(int re tim=0;tim<=5;++tim){
			ll a=p[rand()%17];
			ll b=power(a,t,x);
			for(int re j=1;j<=s;++j){
				ll k=b*b%x;
				if(k==1&&b!=1&&b!=x-1)return false;
				b=k;
			}
			if(b!=1)return false;
		}
		return true;
	}
	
	inline ll Rho(ll p){
		if(p%2==0)return 2;
		ll c=rand()%(p-1)+2,x=1,m=1,t;
		for(int re k=1;;k<<=1){
			ll q=1;
			for(int re s=1;s<=k;++s){
				x=mul(x,x,p)+c;if(x>=p)x-=p;
				q=mul((x-m+p)%p,q,p);
			}
			if((t=gcd(q,p))!=1)return t;
			m=x;
		}
	}
}

ll factor[61];
int tim[61];
int fcnt;
inline void get_factor(ll x){
	using namespace Sieves;
	if(x==1)return ;
	if(isprime(x)){factor[++fcnt]=x;return ;}
	ll p=x;
	while(p==x)p=Rho(p);
	ll g=gcd(p,x/p);
	get_factor(g);
	get_factor(p/g);
	get_factor(x/p/g);
}

ll m;

cs int mod=998244353;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){static ll r;r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b){int res=1;
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}
inline void Mul(int &a,int b){a=mul(a,b);}

int ans;

void dfs(int cur,int c1,int c2){
	if(!c1)return ;
	if(cur>fcnt){
		Inc(ans,mul(c2,dec(power(2,c1),1)));
		return ;
	}
	dfs(cur+1,(ll)c1*(tim[cur]+1)%(mod-1),c2);
	dfs(cur+1,(ll)c1*tim[cur]%(mod-1),add(mod-c2,mod-c2));
	dfs(cur+1,(ll)c1*(tim[cur]-1)%(mod-1),c2);
}

signed main(){
#ifdef zxyoi
	freopen("diyiti.in","r",stdin);
#endif
	srand(time(0));
	std::cin>>m;get_factor(m);
	std::sort(factor+1,factor+fcnt+1);
	fcnt=std::unique(factor+1,factor+fcnt+1)-factor-1;
	ll tmp=m;for(int re i=1;i<=fcnt;++i)
	while(tmp%factor[i]==0)tmp/=factor[i],++tim[i];
	dfs(1,1,1);
	cout<<ans<<"\n";
	return 0;
}

第二题(dierti)

发现状态是有限的。

算一下每个非终止状态出现的概率就行了。

算方案数可以用类似EGF的算阶乘的方式

由于毒瘤出题人卡常,需要用long long 优化取模。

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
    inline char get_char(){
        static cs int Rlen=1<<22|1;
        static char buf[Rlen],*p1,*p2;
        return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
    }
    
    template<typename T>
    inline T get(){
        char c;bool f=0;
        while(!isdigit(c=gc()))f=c=='-';T num=c^48;
        while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
        return f?-num:num;
    }
    inline int getint(){return get<int>();} 
}
using namespace IO;

cs int mod=1e9+7;
inline int power(ll a,int b,int res=1){
	for(;b;b>>=1,a=a*a%mod)(b&1)&&(res=res*a%mod);
	return res;
}

cs int N=1605;

int fac[N],ifac[N];
int n;
ll p[11];int c[11];
ll P;int sum,lim;
int f[11][N][403],coef[N];

inline void init_inv(){
	lim=sum<<2;
	fac[0]=ifac[0]=fac[1]=1;
	for(int re i=2;i<=lim;++i)fac[i]=(ll)fac[i-1]*i%mod;
	ifac[lim]=power(fac[lim],mod-2);
	for(int re i=lim-1;i;--i)ifac[i]=(ll)ifac[i+1]*(i+1)%mod;
}
signed main(){
#ifdef zxyoi
	freopen("dierti.in","r",stdin);
#endif
	n=getint();
	for(int re i=1;i<=n;++i)P+=(p[i]=getint());
	for(int re i=1;i<=n;++i)sum+=(c[i]=getint());
	P=power(P,mod-2);for(int re i=1;i<=n;++i)p[i]=p[i]*P%mod;
	init_inv();
	f[0][0][sum]=1;
	int re i,j,w,k,t,lt;ll v;
	for(i=1;i<=n;++i){
		for(j=0,w=1;j<=lim;++j,w=(ll)w*p[i]%mod)coef[j]=(ll)w*ifac[j]%mod;
		for(j=0;j<=lim;++j)
		for(k=1;k<=sum;++k)if(v=f[i-1][j][k])
		for(t=0,lt=lim-j;t<=lt;++t){
			int g=((t<c[i])?(t-c[i]):(t-c[i])/4)+c[i];
			if(k<=g)break;
			(f[i][j+t][k-g]+=v*coef[t]%mod)%=mod;
		}
	}
	int ans=1;
	for(i=1,v=0;i<=lim;++i,v=0){
		for(j=1;j<=sum;++j)v+=f[n][i][j];v%=mod;
		(ans+=v*fac[i]%mod)%=mod;
	}
	std::cout<<ans<<"\n";
	return 0;
}

第三题(disanti)

xjb推一个式子(开场半个小时推出来)可以知道答案只与两个匹配向量的点积有关,并且在最大化的同时一定满足题目中的限制。

然而SBzxyoi并没有发现是二分图最大权匹配

于是用KM跑一个二分图最大权匹配就可以愉快地TLE辣。

需要非递归版本MMP

代码:

#include<bits/stdc++.h>
#define re register
#define cs const

using std::cerr;
using std::cout;

struct Point{
	int x,y;
	Point(){}
	friend int dot(cs Point &a,cs Point &b){return a.x*b.x+a.y*b.y;}
};

cs int N=507;

int n;

int a[N][N];
namespace KM{
	int wx[N],wy[N];
	int cx[N],cy[N];
	int vs[N],idx;
	int slack[N],pre[N];
	
	void solve(int x){
		cy[0]=x;int y=0,delta,to;
		do{
			vs[y]=idx,x=cy[y],delta=0x3f3f3f3f;
			for(int re j=1;j<=n;++j)
			if(vs[j]!=idx){
				int t=wx[x]+wy[j]-a[x][j];
				if(t<slack[j])slack[j]=t,pre[j]=y;
				if(slack[j]<delta)delta=slack[j],to=j;
			}
			for(int re j=0;j<=n;++j)
			if(vs[j]==idx)wx[cy[j]]-=delta,wy[j]+=delta;
			else slack[j]-=delta;
			y=to;
		}while(cy[y]);
		do{
			cy[y]=cy[pre[y]];
			y=pre[y];
		}while(y);
	}
	
	void solve(){
		for(int re i=1;i<=n;++i){
			for(int re j=1;j<=n;++j)wx[i]=std::max(wx[i],a[i][j]); 
		}
		for(int re i=1;i<=n;++i){
			memset(slack+1,0x3f,sizeof(int)*n);
			++idx;solve(i);
		}
		puts("Yes");
		for(int re i=1;i<=n;++i)cx[cy[i]]=i;
		for(int re i=1;i<=n;++i)cout<<cx[i]<<" ";
	}
}

Point p[N],v[N];

signed main(){
#ifdef zxyoi
	freopen("disanti.in","r",stdin);
#endif
	scanf("%d",&n);
	for(int re i=1;i<=n;++i)scanf("%d%d",&p[i].x,&p[i].y);
	for(int re i=1;i<=n;++i)scanf("%d%d",&v[i].x,&v[i].y);
	for(int re i=1;i<=n;++i)
	for(int re j=1;j<=n;++j){
		a[i][j]=dot(p[i],v[j]);
	}
	KM::solve();
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值