2023.7.2 lby杂题精讲
难啊,很难啊(悲)
A
记 \(f(x)\) 为数字串 \(x\) 中数码众数的出现次数。
求 \(\sum_{i=l}^{r}f(i)\).
\(1\le T\le 10^3\),\(1\le l,r\le 10^{18}\).
最简单的是设状态 \(S=\{c_0,\dots,c_9\}\) 即各数码出现的次数。
变成 \(S=\{a_0,\dots,a_{18}\}\) 为出现次数的个数。
合法的 \(S\) 最多 \(1477\) 种??
这个东西就变成了一个标致的鼠尾地痞。
B
P6811 「MCOI-02」Build Battle 建筑大师
给出 \(n,q\),\(q\) 次给出一个数 \(m\),生成一个长为 \(n\) 的数列 \(\{1,2,\dots,m-1,m,1,2,\dots,(n-1)\text{ mod }m+1\}\).
问这个数列本质不同的子序列个数。
\(1\le n,q\le 10^6\),\(m\le n\).
套路,令一个子序列对应其在数列中最前的一个。
记 \(f_i\) 为第 \(i\) 位结尾的序列数,则
作其前缀和 \(s_i\):
考虑 \(s\) 的组合意义,要么走一步乘 \(2\),要么走 \((m+1)\) 步乘 \(-1\)。
单次复杂度 \(O(\large\frac{n}{m})\),总复杂度 \(O(n\log n)\).
#include<bits/stdc++.h>
#define N 1000010
#define Mod 1000000007
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int qpow(int k,int b,int p){
int ret=1;
while(b){
if(b&1)ret=1ll*ret*k%p;
k=1ll*k*k%p,b>>=1;
}
return ret;
}
int n,q,f[N];
int pw[N],fac[N],inv[N];
int C(int n,int m){
return 1ll*fac[n]*inv[m]%Mod*inv[n-m]%Mod;
}
int main(){
n=read(),q=read();
pw[0]=fac[0]=inv[0]=1;
for(int i=1;i<=n;i++){
pw[i]=pw[i-1]*2%Mod;
fac[i]=1ll*fac[i-1]*i%Mod;
}
inv[n]=qpow(fac[n],Mod-2,Mod);
for(int i=n-1;i;i--)
inv[i]=1ll*inv[i+1]*(i+1)%Mod;
for(int i=1;i<=n;i++){
for(int k=0;k<=n/(i+1);k++)
(f[i]+=(1ll*(k&1?-1:1)*pw[n-(i+1)*k]*C(n-(i+1)*k+k,k)%Mod+Mod)%Mod)%=Mod;
}
while(q--){
printf("%d\n",f[read()]);
}
return 0;
}
C
马在原点,每次走 \(a\times b\) 的矩形,记 \(p(a,b)\) 为马能否遍历每一个整点。
求 \(\sum_{a=1}^{n}\sum_{b=1}^{n}p(a,b)\).
\(n\times T\le 10^{11}\),\(T\le 5\).
这题是 P6860 象棋与马。难绷。
能发现合法的条件为能从 \((0,0)\) 到达 \((0,1)\).
本质不同的行走方案是 \((a,b),(b,a),(-a,b),(-b,a)\),记分别走了 \(x_1,x_2,x_3,x_4\) 次:
由于 \(x-y\) 和 \(x+y\) 都为奇数:
所以答案是
记 \(f(n,m)\) 为
则
同样的记 \(s(n,m)\) 为
则
递推式为
使用杜教筛递归求解,时间复杂度可能是 \(O(n^\frac{2}{3})\).
其实挺快的。
#include<bits/stdc++.h>
#define ll long long
#define LL unsigned long long
#define R 20000001
using namespace std;
int p[R>>2],cnt;bool vis[R];
//LL miu[R];
ll miu[R];
void init(){
miu[1]=1;
for(int i=2;i<R;i++){
if(!vis[i])p[++cnt]=i,miu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<R;j++){
vis[i*p[j]]=true;
if(i%p[j]==0){
miu[i*p[j]]=0;
break;
}
miu[i*p[j]]=-miu[i];
}
}
for(int i=2;i<R;i++)
miu[i]+=miu[i-1];
}
map<ll,LL>mp;
LL query(ll n){
if(n<R)return miu[n];
if(mp[n])return mp[n];
LL ret=1;
for(ll x=2,gx;x<=n;x=gx+1){
gx=n/(n/x);
ret-=(gx-x+1)*query(n/x);
}
return mp[n]=ret;
}
ll T,n;
LL s(ll n,ll m){
if(!n||!m)return 0;
if(n>m)swap(n,m);
LL lst=0,cur=0,ret=0;
for(ll x=1,gx;x<=n;x=gx+1){
gx=min(n/(n/x),m/(m/x));
cur=query(gx),ret+=(cur-lst)*(n/x)*(m/x);
lst=cur;
}
return ret;
}
LL f(ll n,ll m){
if(!n||!m)return 0;
return s(n/2,m)-f(m,n/2);
}
int main(){
cin>>T,init();
while(T--){
cin>>n;
cout<<2*f(n,n)<<endl;
}
return 0;
}
D
心跳,做不了一点。
E
Tastes Like Winning
求共多少个长为 \(n\),值域为 \(\lbrack 1,2^m-1\rbrack\) 的数列 \(a\),满足 \(a_i\) 互不相同且异或和非 \(0\).
\(1\le n,m\le 10^7\).
记 \(f(n)\) 为确定 \(n\) 个的方案数,\(g(n)\) 为这些数异或和为 \(0\) 的方案数,答案即 \(f(n)-g(n)\).
即求第 \(n\) 个数不能取 \(0\) 且不重复的方案数,这一部分是 \(g(n)=f(n-1)-g(n-1)\),但还有重复。
若 \(n\) 与前面一个数重复,故剩下 \(n-2\) 个数异或和为 \(0\),有
尝试理解这个式子。
F
泰裤辣!
一个有向带权图,对于每个点判断其是否在奇权环(可重边,边权多次算)中,若是,输出其所在最小奇权环的权值,或者判断在负奇权环中。
\(n\le 10^3\),\(m\le 10^4\).
将点拆为奇点和偶点,根据边权奇偶性连有向边。
存在奇权环则从奇点能到达偶点。
判断负无穷要用Johnson全源最短路,得学。
G
H
\(n\times m\) 的网格图有 \(k\) 个特殊点。
初始有一个数值 \(s\),只能向下或向右走,经过特殊点时令 \(s\rightarrow\large\lceil\frac{s}{2}\rceil\).
问从左上角走到右下角 \(s\) 的期望值。答案对 \(\rm 1e9+7\) 取模。
\(1\le n,m\le 10^5\),\(0\le k\le 2000\),\(1\le s\le 10^6\).
将点以 \(x,y\) 分别为第一、二关键字排序。
记 \(f_{i,j}\) 为到关键点 \(i\),经过了 \(j\) 个关键点的方案数。
记 \(p(k,i)\) 为从关键点 \(k\) 到关键点 \(i\) 的方案数。
\(\large f_{i,j}=\sum\limits_{k}f_{k,j-1}\cdot p(k,i)\)?会算重。
把 \(f_{i,j}\) 的意义改为到点 \(i\) 时至少经过 \(j\) 个点的方案数。
恰好 \(j\) 次即 \(f_{i,j}-f_{i,j+1}\).
注意得到至少之后应直接算出恰好的,不然会寄掉。
-
\(p(k,i)\) 的计算应为 \(\Large\binom{x_i+y_i-x_k-y_k}{x_i-x_k}\).
-
对于经过关键点个数 \(> \log s\) 的就是总数减去经过 \(\le \log s\) 个方案数,故总时间复杂度 \(O(n^2\log s)\).
#include<bits/stdc++.h>
#define ll long long
#define N 2010
#define R 200001
#define L 22
#define Mod 1000000007
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
ll qpow(ll k,ll b,ll p){
ll ret=1;
while(b){
if(b&1)(ret*=k)%=p;
(k*=k)%=p,b>>=1;
}
return ret;
}
ll fac[R],inv[R];
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<R;i++)
fac[i]=fac[i-1]*i%Mod;
inv[R-1]=qpow(fac[R-1],Mod-2,Mod);
for(int i=R-2;i;i--)
inv[i]=inv[i+1]*(i+1)%Mod;
}
ll C(int n,int m){
if(n<m)return 0;
// printf("C n m %d %d %lld\n",n,m,fac[n]*inv[n-m]%Mod*inv[m]%Mod);
return fac[n]*inv[n-m]%Mod*inv[m]%Mod;
}
int n,m,k,s;
struct node{
int x,y;
bool operator<(const node &t)const{
return x!=t.x?x<t.x:y<t.y;
}
}a[N];
ll f[N][L+5];
int now[L];
int main(){
n=read(),m=read(),k=read(),s=read();
init(),a[0]=(node){1,1},a[k+1]=(node){n,m};
for(int i=1,x,y;i<=k;i++){
x=read(),y=read();
a[i]=(node){x,y};
}
sort(a+1,a+1+k),f[0][0]=1;
for(int i=1;i<=k+1;i++){
for(int j=0;j<i;j++){
if(a[j].x>a[i].x||a[j].y>a[i].y)continue;
ll t=C(a[i].x+a[i].y-a[j].x-a[j].y,a[i].x-a[j].x);
for(int p=1;p<=L;p++)
(f[i][p]+=f[j][p-1]*t%Mod)%=Mod;
}
for(int p=1;p<=L;p++)
(f[i][p]+=Mod-f[i][p+1])%=Mod;
}
now[1]=s;
for(int i=2;i<=L;i++)
now[i]=(now[i-1]+1)/2;
ll tot=C(n+m-2,n-1),cur=tot,sum=0;
for(int i=1;i<=L;i++){
(sum+=f[k+1][i]*now[i]%Mod)%=Mod;
(cur+=Mod-f[k+1][i])%=Mod;
}
(sum+=cur)%=Mod;
printf("%lld\n",sum*qpow(tot,Mod-2,Mod)%Mod);
return 0;
}
I
J
K
L
动态增减边,选择四个不同的点 \(a,b,c,d\),使 \(w_{a\rightarrow b}+w_{c\rightarrow d}\) 最小,输出这个值。
\(4\le n,m\le 10^5\),\(1\le w\le 10^9\),\(0\le q\le 10^5\).
每条边只有三条边有效,共两种情况:
-
选择两条不相交的边
-
选择节点数为 \(4\) 的菊花图
把一条边所连的最小的三条边塞进 \(\rm set\) 里,拉出来一些边暴力做。
代码答辩。贺完跑路。
#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define M 200010
#define inf 2e9
#define pii pair<int,int>
#define mp make_pair
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n,m,q;
int U[M],V[M],W[M];
set<pii>s[N],S;
map<pii,int>E;
int lim(int u){
return s[u].size()<3?inf:next(s[u].begin(),2)->first;
}
void pop(int u){
auto it=s[u].begin();
for(int i=0;i<min(3,(int)s[u].size());i++,it++)
if(S.count(*it))S.erase(*it);
}
void push(int u){
auto it=s[u].begin();
for(int i=0;i<min(3,(int)s[u].size());i++,it++)
if(it->first<=min(lim(U[it->second]),lim(V[it->second])))
S.insert(*it);
}
void solve(){
ll ret=(ll)S.begin()->first+next(S.begin(),1)->first+next(S.begin(),2)->first;
int top=S.begin()->second,id,x,y;
for(auto it=next(S.begin());it!=S.end();it++){
id=it->second;
if(U[id]!=U[top]&&U[id]!=V[top]&&V[id]!=U[top]&&V[id]!=V[top]){
ret=min(ret,(ll)W[id]+W[top]);
for(auto a=next(S.begin());a!=it;a++)
for(auto b=next(a);b!=it;b++){
x=a->second,y=b->second;
if(U[x]!=U[y]&&U[x]!=V[y]&&V[x]!=U[y]&&V[x]!=V[y])
ret=min(ret,(ll)W[x]+W[y]);
}
printf("%lld\n",ret);
return;
}
}
for(auto a=next(S.begin());a!=S.end();a++)
for(auto b=next(a);b!=S.end();b++){
x=a->second,y=b->second;
if(U[x]!=U[y]&&U[x]!=V[y]&&V[x]!=U[y]&&V[x]!=V[y]){
ret=min(ret,(ll)W[x]+W[y]);
break;
}
}
printf("%lld\n",ret);
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
U[i]=read(),V[i]=read(),W[i]=read();
if(U[i]>V[i])swap(U[i],V[i]);
s[U[i]].insert(mp(W[i],i)),s[V[i]].insert(mp(W[i],i));
E[mp(U[i],V[i])]=i;
}
for(int i=1;i<=n;i++)push(i);
q=read(),solve();
for(int opt,u,v,id;q;q--){
opt=read();
if(!opt){
u=read(),v=read();
if(u>v)swap(u,v);
pop(u),pop(v),id=E[mp(u,v)];
s[u].erase(mp(W[id],id)),s[v].erase(mp(W[id],id));
E[mp(u,v)]=0,push(u),push(v);
}
else{
U[++m]=read(),V[m]=read(),W[m]=read();
if(U[m]>V[m])swap(U[m],V[m]);
pop(U[m]),pop(V[m]),E[mp(U[m],V[m])]=m;
s[U[m]].insert(mp(W[m],m)),s[V[m]].insert(mp(W[m],m));
push(U[m]),push(V[m]);
}
solve();
}
return 0;
}