再次自闭
开题顺序3,2,1。
写完T2后发现T3锅了,然后重写。
T1暴力没打完。
竟然卡进了前十QwQ。(涨了100+)
T2
大凯的疑惑
给定\(a,b\),求使\(ax+by=c\)没有非负整数\((x,y)\)解的前\(k\)大的正整数\(c\)。
保证\(40\%\)的数据有\(a,b \leq 1000\)。
保证另外\(50\%\)的数据有\(k \leq 10^5\)
保证\(100\%\)的数据有\(a,b \leq 10^9, k\leq 5 \times 10^6, (a,b)=1\)。
暴力跑一个背包,跑到\(ab-a-b\)
从大往小枚举异或即可
用上面的背包打表发现,
一个数不能被表示出来它一定是 形式.
不会证明
(口胡一个证明,
首先\(ab-a-b\)不能被表示出来,
然后如果一个数不能被表示出来,,也不能被表示,
所以必要性显然,
充分性,
因为\((a,b)=1\)所以任何数都有整数\((x,y)\) 解,
所以某不能被表示出来的\(c=ax_0+by_0\)\((x_0,y_0\in Z)\)
只要证必有一个\(x_0,y_0\)小于0即可.
因为假设两种钱每种最少要拿一次(也就是不能不拿),那么不能凑成的最大钱数 \(k=a\times b\) 证明康这里
所以\(c=ab-(u+1)a-(v+1)b\)必有\(x_0或y_0<0\)
)
所以考虑把\(ab-a-b\)塞到一个set
里,不断取出最大值,\(-a,-b\)后再塞入set
里,取k次即可.
复杂度\(O(k\ \log\ k)\)
期望得分90,实际得分100??
回忆一下noip2016蚯蚓
考虑开两个队列,一个是操作\(-a\)之后的,一个是操作\(-b\)之后的.
每次取两队首进行取较大的进行操作.
注意:为了防止重复,\(-a\)队列里的可向\(-b\)内转移,但\(-b\)后的不能向里转移.
给出期望90实际100的代码.
#include<bits/stdc++.h> using namespace std; typedef long long ll; set<ll > s; ll a,b,k; ll ans; int main(){ scanf("%lld%lld%lld",&a,&b,&k); s.insert(-(a*b-a-b)); ll cnt=1; while(!s.empty()&&cnt<=k){ cnt++; ll x=-*s.begin(); ans^=x; s.erase(s.begin()); if(x>a)s.insert(a-x); if(x>b)s.insert(b-x); } printf("%lld",ans); return 0; }
给出杜教的(期望)100代码
#include <bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) const int N=10100000; ll a,b,q1[N],q2[N],ans; int k; int main() { scanf("%lld%lld%d",&a,&b,&k); assert(gcd(a,b)==1); if (a>b) swap(a,b); ll w=a*b-a-b; int h1=0,t1=1,h2=0,t2=0; q1[0]=w; rep(i,0,k) { if (h1==t1&&h2==t2) break; if (h2==t2||(h1!=t1&&q1[h1]>q2[h2])) { ll z=q1[h1++]; ans^=z; if (z-a>0) q1[t1++]=z-a; if (z-b>0) q2[t2++]=z-b; } else { ll z=q2[h2++]; ans^=z; if (z-b>0) q2[t2++]=z-b; } } printf("%lld\n",ans); }
T3
一开始一个\(1\)到\(n\)排列中的所有元素都是不确定的,现在我们要逐个确定下来\(q\)个元素的位置\(p_u=v\)
我们想知道每次给定的元素确定下来的之后,有多少种不同的错排.
保证\(100\%\)的数据有\(n\leq 5 \times 10^3, q\leq n\),保证\(u\neq v\),并且所有的\(u\)与\(v\)是两两不同的。
做法
将每个\(p_u\)和\(v\)连一条有向边,问题转化为有多少种图满足没有自环.
连成的图一定是若干个环形成的.
元素确定下来,每一次连边都会形成一条链.
若形成一个环,那么这个环可以直接去掉对答案没有影响.
假设目前已经有\(a\)条单链,\(b\)条个单点.
在这里,链和链是本质相同的,和单点本质不同.
因为点不能和自己连边,而一条链可以自闭成一个环.
- 考虑容斥,枚举有至少多少个单点连向自己.
\(ans=\sum\limits_{i=0}^b(-1)^b\dbinom{b}{i}\cdot (a+b-i)!\) - 枚举有多少环自闭了.
\(ans=\sum\limits_{i=0}^a\dbinom{a,i}D(a+b-k)\)
给出方法二的代码实现
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=5007; const ll mod=1e9+7; ll n,q; ll f[maxn],g[maxn],d[maxn]; bool va[maxn],vb[maxn]; ll qpow(ll a,ll b){ ll rt=1; while(b){ if(b&1)rt=(rt*a)%mod; a=(a*a)%mod; b>>=1; } return rt; } ll p(ll n,ll m){ if(n<m)return 0; if(m==0)return 1; return f[n]*g[n-m]%mod; } ll C(ll n,ll m){ if(n<m)return 0; if(n==m||m==0)return 1; return f[n]*g[m]%mod*g[n-m]%mod; } int main(){ f[0]=1; for(int i=1;i<=5000;i++)f[i]=f[i-1]*i%mod; g[5000]=qpow(f[5000],mod-2); for(int i=5000;i>=1;i--)g[i-1]=g[i]*i%mod; d[0]=1,d[1]=0; for(int i=2;i<=5000;i++)d[i]=(i-1)*(d[i-1]+d[i-2])%mod; scanf("%lld%lld",&n,&q); printf("%lld\n",d[n]%mod); ll a=0,b=n; for(int i=1;i<=q;i++){ int u,v; scanf("%d%d",&u,&v); if(vb[u]&&va[v])a--; else if(vb[u]&&!va[v])b--; else if(!vb[u]&&va[v])b--; else a++,b-=2; va[u]=true,vb[v]=true; ll ans=0; for(int j=0;j<=a;j++){ ans=(ans+C(a,j)%mod*d[a+b-j]%mod)%mod; } printf("%lld\n",ans); } return 0; }
T1
\(T\)组数据,求\(a^x \equiv b \pmod {p^e}\)的最小非负整数解。
对于\(20\%\)的数据,保证 \(1 \leq T \leq 10, p^e \leq 10^5\)。
对于\(50\%\)的数据,保证 \(1 \leq T \leq 10, p^e \leq 3^{21}\)。
对于另外\(20\%\)的数据,保证 \(1 \leq T \leq 1000, p^e \leq 3^{21}\),并且所有的\(a, p, e\)都是相同的。
对于\(100\%\)的数据,保证\(1 \leq T \leq 1000, 1\leq a, b \leq p^e-1, p \nmid a, p \nmid b, 3\leq p\leq 50, e \geq 1, p^e \leq 10^{18}, p\)是质数。
20pts
暴力枚举到\(\phi(p^e)\)即可
50pts
因为\(p \nmid a, p \nmid b\)
直接BSGS即可
70pts
在50pts基础上
只预处理一次hash表即可.
块大小开(O(\sqrt{\dfrac{p}{n}}))即可
100pts
因为\(p\in prime\) \(g_{p^e}=g_p\)
求出原根\(g\)
对方程两边取\(g\)的对数
得到\(\log_g{a}x\equiv\log_g{b}(mod \ p^e)\)
问题转化为求在来次扩欧
即求
设
得到
又因为
所以枚举\(X\equiv X_0+|_{i=0 \to p}(p-1)*i(mod \ p(p-1))\)
令解为
一直像这样递推到即可
给出代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int prime[14]={3,5,7,11,13,17,19,23,29,31,37,41,43,47}; const int root[14]={2,2,3,2,2,3,2,5,2,3,2,6,3,5}; ll exgcd(ll a,ll b,ll &x,ll &y){ if(!b) return x=1,y=0,a; else{ ll rt=exgcd(b,a%b,y,x); y-=(a/b)*x; return rt; } } inline ll mul(ll x,ll y,ll p){ ll k=(ll)((1.0l*x*y)/(1.0l*p)),t=x*y-k*p; t-=p;while(t<0)t+=p; return t; } ll qpow(ll a,ll b,ll p) { ll rt=1; while(b){ if(b&1)rt=mul(rt,a,p); a=mul(a,a,p); b>>=1; } return rt; } ll findg(ll g,ll a,ll p,ll e){ ll mod=p,step=1,ans=0; for(int i=0;i<e;i++){ ll now=qpow(g,ans,mod); ll base=qpow(g,step,mod); ll to=a%mod; while(now!=to){ now=mul(now,base,mod); ans+=step; } if(step==1)step=p-1; else step*=p; mod*=p; } return ans; } void solve(){ ll a,b,p,e,rt; scanf("%lld%lld%lld%lld",&a,&b,&p,&e); for(int i=0;i<14;i++)if(prime[i]==p)rt=root[i]; a=findg(rt,a,p,e); b=findg(rt,b,p,e); p=(p-1)*qpow(p,e-1,LLONG_MAX); ll x,y; ll gcd=exgcd(a,p,x,y); if(b%gcd){ printf("-1\n"); return ; } x=mul(x,b/gcd,p); p/=gcd; x=(x%p+p)%p; printf("%lld\n",x); } int main(){ int t; scanf("%d",&t); while(t--){ solve(); } return 0; }