第一题(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;
}