2026暑期牛客多校3 2026-7-24

sol 7

A:

注意到只有10和结尾的1会对答案增加一次贡献

相邻两位的情况只存在4种 00 01 10 11,可以直接状态压缩

记录原数组中每个位和下一位的状态的种数,以及以0/1结尾的个数

操作时,记录当前位的变化

具体的,在该位,0会变成什么,1会变成什么

遍历4个状态,如果该位的这个状态在该次变化后变成10,累加状态数

记得算上结尾状态

code:

#include<bits/stdc++.h>

#define int long long

#define inf 0x3f3f3f3f3f3f3f

#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

#define cnot cout<<"NO"<<"\n"

#define cyes cout<<"YES"<<"\n"

#define cans cout<<ans<<"\n"

#define pb push_back

#define x0 first

#define y0 second

#define lc p<<1

#define rc p<<1|1

#define mem(a,b) memset(a,b,sizeof(a))

#define sp(x) fixed<<setprecision(x)

#define all(v) v.begin(),v.end()

#define fr(i,st,ed) for(int i=st;i<=ed;i++)

#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)

using namespace std;

typedef pair<int,string>Pis;

typedef pair<int,int>Pii;

typedef pair<string,string>Pss;

const int N=35,mod=1e9+7,M=1e6+10;

int lowbit(int x){

return x&(-x);}

int BN4;

//00

//01

//10

//11

void solve(){

memset(B,0,sizeof(B));

int L0=0,L1=0;

int n;

cin>>n;

vector<int>a(n+1);

fr(i,1,n){

cin>>ai;

if(ai&1)L1++;

else L0++;

fr(j,0,29){

int v1=(ai>>j)&1;

int v2=(ai>>(j+1))&1;

int msk=(v2<<1)|v1;

Bjmsk++;

}

}

int Mask0=0;

int Mask1=(1<<30)-1;

int type,x;

int m;

cin>>m;

while(m--){

cin>>type>>x;

int ans=0;

if(type==1){

Mask0&=x;

Mask1&=x;

}

else if(type==2){

Mask0|=x;

Mask1|=x;

}

else{

Mask0^=x;

Mask1^=x;

}

fr(i,0,29){

if(i==0){

if(Mask0&1)ans+=L0;

if(Mask1&1)ans+=L1;

}

fr(j,0,3){

int v1=j&1;

int v2=(j>>1)&1;

int t1,t2;

if(v1==0)t1=(Mask0>>i)&1;

else t1=(Mask1>>i)&1;

if(v2==0)t2=(Mask0>>(i+1))&1;

else t2=(Mask1>>(i+1))&1;

if(t1==0&&t2==1)ans+=Bij;

}

}

cans;

}

}

signed main(){

GG;

int _t=1;

//cin>>_t;

while(_t--){

solve();

}

}

B

推导最难的数学题

P1

若在恰好喝完 m 瓶时一共中了 w 次奖,则手中剩余的钱为 n-m+cw 。 题目要求此时恰好花光,因此必须有 m-n=cw 所以当 m<n|(m-n)%c!=0时,输出0,否则中奖次数已经唯一确定:w=(m-n)/c,接下来只需统计:在长度为 m、恰有 w 个中奖的所有结果序列中,有多少个不会在第 m 瓶之前把钱花光

P2

如果你了解过卡特兰推广:

普通 Dyck 路径的步长为

+1, −1.+1,−1.

它对应的部分和满足:

Sj≥0,Sn=0.Sj​≥0,Sn​=0.

卡特兰数统计的是这类不跌破初始水平的路径。

所以我们希望通过"前缀和非负"解决这个问题,

把中奖记为 W,未中奖记为 L。喝完一瓶后,手中可继续购买的饮料数的变化量为:

W: c-1 L: -1

记原序列的前缀和为 S_t 。喝完前 t 瓶后,剩余饮料数为 A_t=n+S_t

一个序列合法,当且仅当:

A_t>0 (0<=t<m)

A_m=0

由 cw =m-n 可知总变化量 S_m=-n ,故第二个条件自动满足。 将序列反转并把每一项取相反数。新序列的两种步长为:

W -> 1-c

L -> 1

它们均不超过 1,总和为 n。新序列的前缀和 R_k 满足 R_k=A_{m-k} 因此原序列合法,当且仅当新序列的所有非空前缀和均为正。

对以上内容的直观理解就是:从后往前不能出现钱数为负数的情况,因为从前往后初始值有n,不好用前缀和,所以我们考虑反过来,从花完钱往开始推;

P3:

相信没多少人听过题解里面的Raney 引理,但我们可以自己推导:

如图:

code:

#include<bits/stdc++.h>

#define int long long

#define inf 0x3f3f3f3f3f3f3f

#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

#define cnot cout<<"NO"<<"\n"

#define cyes cout<<"YES"<<"\n"

#define cans cout<<ans<<"\n"

#define pb push_back

#define x0 first

#define y0 second

#define lc p<<1

#define rc p<<1|1

#define mem(a,b) memset(a,b,sizeof(a))

#define sp(x) fixed<<setprecision(x)

#define all(v) v.begin(),v.end()

#define fr(i,st,ed) for(int i=st;i<=ed;i++)

#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)

using namespace std;

typedef pair<int,string>Pis;

typedef pair<int,int>Pii;

typedef pair<string,string>Pss;

const int N=2e6+10,mod=998244353,M=1e6+10;

int lowbit(int x){

return x&(-x);}

int qmi(int a,int b){

int res=1LL;

while(b){

if(b&1)res=res*a%mod;

b>>=1;

a=a*a%mod;

}

return res;

}

int fN,invfN;

void init(){

f0=1;

fr(i,1,N-1){

fi=fi-1*i%mod;

}

invfN-1=qmi(fN-1,mod-2);

for(int i=N-2;i>=0;i--){

invfi=invfi+1*(i+1)%mod;

}

}

int C(int n,int m){

if(n<m||m<0||n<0)return 0;

return fn%mod*invfm%mod*invfn-m%mod;

}

void solve(){

int n,m,c,a,b;

cin>>n>>m>>c>>a>>b;

if((m-n)%c!=0){

cout<<0<<"\n";

return;

}

int x=(m-n)/c;

if(x>m||x<0){

cout<<0<<"\n";

return ;

}

int p=a%mod*qmi(b,mod-2)%mod;//中

int q=((b-a+mod)%mod+mod)%mod*qmi(b,mod-2)%mod;//不中

int ans=n*qmi(m,mod-2)%mod*qmi(p,x)%mod*qmi(q,m-x)%mod*C(m,x)%mod;

cans;

}

signed main(){

GG;

int _t=1;

cin>>_t;

init();

while(_t--){

solve();

}

}

F

矩阵快速幂/状态压缩优化

注意到n很小,m很大,很容易想到矩阵快速幂处理每一列多个状态对应的转移;

朴素的思路是dfs将所有可能的列状态找出来并编码,然后chang第一个状态第二个状态=1/0表示能否转移

但发现总共的状态数有1500多种,全部朴素转移会超时

考虑到 0 1 2 0 和 1 2 0 1 本质上是同一种状态,我们将这种相对顺序相同的并为一种,分别统计这样的状态有几种,此时的st状态表示该编码的状态有多少种,chang第一种状态第二种状态\[\]=num表示第一种状态转移到第二种有多少种情况;

将st*chang^m-1,得到的就是最后每一列每一种状态的值,累加既是答案

code:

#include<bits/stdc++.h>

#define int long long

#define inf 0x3f3f3f3f3f3f3f

#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

#define cnot cout<<"NO"<<"\n"

#define cyes cout<<"YES"<<"\n"

#define cans cout<<ans<<"\n"

#define pb push_back

#define x0 first

#define y0 second

#define lc p<<1

#define rc p<<1|1

#define mem(a,b) memset(a,b,sizeof(a))

#define sp(x) fixed<<setprecision(x)

#define all(v) v.begin(),v.end()

#define fr(i,st,ed) for(int i=st;i<=ed;i++)

#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)

using namespace std;

typedef pair<int,string>Pis;

typedef pair<int,int>Pii;

typedef pair<string,string>Pss;

const int N=35,mod=998244353,M=1e6+10;

int lowbit(int x){

return x&(-x);}

vector<vector<int> >vec;

vector<vector<int>>allvec;

int n,m;

void dfs(vector<int>tmp){

if(tmp.size()==n){

allvec.pb(tmp);

return;

}

fr(i,0,2){

if(tmp.empty()||i!=tmp.back()){

tmp.pb(i);

dfs(tmp);

tmp.pop_back();

}

}

}

vector<int>ER(const vector<int>&tmp){

int id3={-1,-1,-1};

int cnt=0;

vector<int>res;

res.reserve(n);

for(int x:tmp){

if(idx==-1)idx=cnt++;

res.pb(idx);

}

return res;

}

int EC(const vector<int>&tmp){

int res=0;

for(int x:tmp){

res=res*3+x;

}

return res;

}

vector<vector<int>>chang;

vector<vector<int>> mul(vector<vector<int>> a,vector<vector<int>> b){

int n=a.size();

int m=b0.size();

int k=a0.size();

vector<vector<int>>ans(n,vector<int>(m));

fr(i,0,n-1){

fr(j,0,m-1){

fr(c,0,k-1){

ansij=(int)(((int)aic*bcj+ansij)%mod);

}

}

}

return ans;

}

vector<vector<int>> power(vector<vector<int>> m,int p){

int n=m.size();

vector<vector<int>>ans(n,vector<int>(n));

fr(i,0,n-1){

ansii=1;

}

for(;p!=0;p>>=1){

if((p&1)!=0){

ans=mul(ans,m);

}

m=mul(m,m);

}

return ans;

}

bool check(const vector<int>&a,const vector<int>&b){

fr(i,0,n-1)if(ai==bi)return 0;

return 1;

}

void solve(){

vec.clear();

allvec.clear();

chang.clear();

cin>>n>>m;

vector<int>tmp;

dfs(tmp);

/*

int sz=vec.size();

fr(i,0,sz-1){

for(int x:veci){

cout<<x<<" ";

}

cout<<"\n";

}

*/

//cout<<vec.size();

map<int,int>mp;

for(auto&v:allvec){

vector<int>nw=ER(v);

int ID=EC(nw);

if(!mp.count(ID)){

mpID=(int)vec.size();

vec.pb(nw);

}

}

int sz=(int)vec.size();

chang.resize(sz,vector<int>(sz,0));

/*

for(int i=0;i<m;i++){

for(int j=0;j<m;j++){

bool f=1;

for(int k=0;k<n;k++){

if(vecik==vecjk){

f=0;

break;

}

}

if(f)changij=1;

}

}

*/

fr(i,0,sz-1){

for(auto&v:allvec){

if(!check(veci,v))continue;

int ID=EC(ER(v));

changimp\[ID]++;

}

}

vector<int>st(sz,0);

vector<vector<int>>St;

St.pb(st);

for(auto&v:allvec){

stmp\[EC(ER(v))]++;

}

if(m==1){

cout<<allvec.size()<<"\n";

return ;

}

chang=power(chang,m-1);

//vector<vector<int>>ans=mul(St,chang);

vector<int>Ans(sz,0);

fr(i,0,sz-1){

fr(j,0,sz-1){

Ansj=(Ansj+sti*changij)%mod;

}

}

int Res=0;

for(int x:Ans)Res=(Res+x)%mod;

cout<<Res;

}

signed main(){

GG;

int _t=1;

//cin>>_t;

while(_t--){

solve();

}

}

G

二维差分/前后缀预处理

固定一个数值 x。若两个值为 x 的格子分别在 (r1,c1) 与 (r2,c2)

r1<r2,c1<c2

它们会标记矩形 r1,r2 x c1,c2。不同数值产生的矩形只需取并集,因此可以独立处理每一种数值,最后用二 维差分叠加所有矩形。

将 x 出现过的行按升序记为 x1<x2<x3<.....<xn

在同一行中,只保留 x 出现位置的最小列 mni 与最大列 mxi 即可

具体的,对于每一个元素,创建一个{行,行最小列,行最大列}的三元组,每次判断当前行是否存在,若不存在则加入当前列为最小/最大列,若存在则跟新最大列

考虑第 rt 行和第 r(t+1) 行之间的任意一条横向分界线。一个跨过该分界线的合法矩形,其左上角只能来自分界线 上方、右下角只能来自分界线下方。

Lt=min(mn1,mn2,...,mnt) Rt=max(mxt+1,mxt+2,...,mxk)

维护前/后缀,若合法,则向二维差分中跟新

code:

#include <bits/stdc++.h>

using namespace std;

using LL = long long;

#define endl "\n"

LL mod=998244353;

LL ksm(LL a,LL n){

LL res=1;

while(n){

if(n&1)res=res*a%mod;

n/=2;

a=a*a%mod;

}

return res%mod;

}

void solve(){

LL n,m;

cin>>n>>m;

vector<vector<LL>>a(n+1,vector<LL>(m+1));

LL k=1500;

vector<vector<pair<LL,LL>>>w(n*m+1);

for(int i=1;i<=n;i++){

for(int j=1;j<=m;j++){

cin>>aij;

wa\[ij].push_back({i,j});

}

}

vector<vector<LL>>ans(n+2,vector<LL>(m+2)),pre(n+2,vector<LL>(m+2)),sum1(n+2,vector<LL>(m+2)),sum2(n+2,vector<LL>(m+2));

for(int i=1;i<=n*m;i++){

if(wi.size()<2)continue;

if(wi.size()<=k){

// cout<<i<<endl;

for(autox1,y1:wi){

for(autox2,y2:wi){

// if(x1==x2||y1==y2)continue;

// if(x1>=x2||y1>=y2)continue;

// cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<endl;

if(x1<x2&&y1<y2){

prex1y1++;

prex1y2+1--;

prex2+1y1--;

prex2+1y2+1++;

}

}

}

}

else{

for(int j=1;j<=n;j++){

for(int l=1;l<=m;l++)sum1jl=0,sum2jl=0;

}

for(int j=1;j<=n;j++){

for(int l=1;l<=m;l++){

sum1jl+=sum1j-1l+sum1jl-1-sum1j-1l-1;

if(ajl==i)sum1jl++;

}

}

for(int j=n;j>=1;j--){

for(int l=m;l>=1;l--){

sum2jl+=sum2j+1l+sum2jl+1-sum2j+1l+1;

if(ajl==i)sum2jl++;

}

}

for(int j=1;j<=n;j++){

for(int l=1;l<=m;l++){

if(sum1j-1l-1 && sum2jl) ansjl=1;

if(sum1jl && sum2j+1l+1) ansjl=1;

if(sum1jl-1 - sum1j-1l-1 && sum2j+1l - sum2j+1l+1) ansjl=1;

if(sum1j-1l - sum1j-1l-1 && sum2jl+1 - sum2j+1l+1) ansjl=1;

}

}

}

}

for(int i=1;i<=n;i++){

for(int j=1;j<=m;j++){

preij+=prei-1j+preij-1-prei-1j-1;

}

}

for(int i=1;i<=n;i++){

for(int j=1;j<=m;j++){

if(ansij||preij)cout<<1;

else cout<<0;

}

cout<<endl;

}

}

int main(){

ios::sync_with_stdio(0);

cin.tie(0),cout.tie(0);

LL T=1;

// build();

// cin>>T;

while(T--){

solve();

}

}

J

队友写的神秘思路

#include <bits/stdc++.h>

using namespace std;

using LL = long long;

#define endl "\n"

LL mod=998244353;

LL ksm(LL a,LL n){

LL res=1;

while(n){

if(n&1)res=res*a%mod;

n/=2;

a=a*a%mod;

}

return res%mod;

}

vector<LL>fa(1e6+10),ans(1e6+10);

LL find(int x){

if(fax!=x)fax=find(fax);

return fax;

}

void union1(LL u,LL v){

LL x=find(u);

ansx=v;

fax=v;

}

void solve(){

LL n,q;

cin>>n>>q;

vector<vector<LL>>edag(n+1);

vector<LL>pa(n+1,n+1),dep(n+2),dep1(n+1);

for(int i=1;i<=n;i++)fai=i,ansi=n+1;

depn+1=1e9;

for(int i=2;i<=n;i++){

LL x;

cin>>x;

edagx.push_back(i);

edagi.push_back(x);

}

auto dfs=\&(auto&&self,int x,int fa1)->void{

if(fa1!=-1)depx=depfa1+1;

for(int y:edagx){

if(y==fa1)continue;

self(self,y,x);

}

};

dfs(dfs,1,-1);

priority_queue<tuple<LL,LL,LL>>pq;

while(q--){

int u,v;

cin>>u>>v;

pq.push({depv,u,v});

}

while(!pq.empty()){

autosum,u,v=pq.top();

//cout<<u<<" "<<v<<" "<<sum<<endl;

pq.pop();

union1(u,v);

//for(int i=1;i<=n;i++)cout<<ansi<<" ";

//cout<<endl;

}

vector<vector<LL>>edag1(n+1);

for(int i=2;i<=n;i++){

if(ansi==n+1||ansi==i)ansi=1;

//cout<<ansi<<" ";

edag1ans\[i].push_back(i);

edag1i.push_back(ansi);

}

LL ans1=0;

auto dfs1=\&(auto&&self,int x,int fa1)->void{

if(fa1!=-1)dep1x=dep1fa1+1;

ans1+=dep1x;

for(int y:edag1x){

if(y==fa1)continue;

self(self,y,x);

}

};

dfs1(dfs1,1,-1);

cout<<ans1<<endl;

}

int main(){

ios::sync_with_stdio(0);

cin.tie(0),cout.tie(0);

LL T=1;

// build();

// cin>>T;

while(T--){

solve();

}

}

K

叉积

签到

#include<bits/stdc++.h>

#define int long long

#define inf 0x3f3f3f3f3f3f3f

#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

#define cnot cout<<"NO"<<"\n"

#define cyes cout<<"YES"<<"\n"

#define cans cout<<ans<<"\n"

#define pb push_back

#define x0 first

#define y0 second

#define lc p<<1

#define rc p<<1|1

#define mem(a,b) memset(a,b,sizeof(a))

#define sp(x) fixed<<setprecision(x)

#define all(v) v.begin(),v.end()

#define fr(i,st,ed) for(int i=st;i<=ed;i++)

#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)

using namespace std;

typedef pair<int,string>Pis;

typedef pair<int,int>Pii;

typedef pair<string,string>Pss;

const int N=35,mod=1e9+7,M=1e6+10;

int lowbit(int x){

return x&(-x);}

int calc(Pii a,Pii b,Pii c){

return ((b.x0-a.x0)*(c.y0-a.y0)-(b.y0-a.y0)*(c.x0-a.x0));

}

void solve(){

int n;

cin>>n;

vector<Pii>a(n+5);

fr(i,1,n)cin>>ai.x0>>ai.y0;

fr(i,2,n-1){

int x=calc(ai-1,ai,ai+1);

if(x>0)cout<<"LEFT"<<" ";

else if(x<0)cout<<"RIGHT"<<" ";

else cout<<"STRAIGHT"<<" ";

}

cout<<"\n";

}

signed main(){

GG;

int _t=1;

cin>>_t;

while(_t--){

solve();

}

}

L

博弈dp

签到

博弈模型:旗帜在网格上移动,只能往更高的格子走,因此所有可能的移动构成一个有向无环图(高度严格递增,不 可能成环)。这是一个单 token 在 DAG 上的正常博弈(normal play,无路可走者输),不是多个独立游戏的和,所 以不需要 Sprague-Grundy 理论,只需判定每个格子的胜/负状态。

胜负定义(经典):

一个格子是必胜(First),当且仅当它存在至少一个严格更高的相邻格子,且该相邻格子是必败的(把必败 局面扔给对手)

否则它是必败(Second)。特别地,局部最大值(没有更高邻居)必败

按高度递减 DP:因为每条边都由低处指向高处,所以按高度从高到低处理每个格子时,它的所有严格更高邻居都已 被计算完毕。直接迭代:

#include <bits/stdc++.h>

using namespace std;

using LL = long long;

#define endl "\n"

LL mod=998244353;

LL ksm(LL a,LL n){

LL res=1;

while(n){

if(n&1)res=res*a%mod;

n/=2;

a=a*a%mod;

}

return res%mod;

}

void solve(){

LL n,m;

cin>>n>>m;

vector<vector<LL>>a(n+1,vector<LL>(m+1));

priority_queue<tuple<LL,LL,LL>>pq;

for(int i=1;i<=n;i++){

for(int j=1;j<=m;j++){

cin>>aij;

pq.push({aij,i,j});

}

}

LL dx4={0,-1,0,1},dy4={1,0,-1,0};

vector<vector<LL>>ans(n+1,vector<LL>(m+1));

while(!pq.empty()){

autosum,x,y=pq.top();

// cout<<x<<" "<<y<<endl;

pq.pop();

//ansxy=1;

for(int i=0;i<4;i++){

int x1=x+dxi,y1=y+dyi;

if(x1<1||x1>n||y1<1||y1>m)continue;

// cout<<x1<<" "<<y1<<endl;

if(ax1y1>axy){

if(ansx1y1==0)ansxy=1;

}

}

//cout<<endl;

}

LL q;

cin>>q;

while(q--){

LL r,c;

cin>>r>>c;

if(ansrc)cout<<"First"<<endl;

else cout<<"Second"<<endl;

}

}

int main(){

ios::sync_with_stdio(0);

cin.tie(0),cout.tie(0);

LL T=1;

// build();

cin>>T;

while(T--){

solve();

}

}

相关推荐
2501_937860941 小时前
Java 集合底层深度剖析:Map 与 Set、二叉搜索树、哈希表全解
java·数据结构·散列表
geats人山人海2 小时前
算法基础第二讲 基础算法下
算法
Hommy882 小时前
【剪映小助手】字符串列表转对象接口(Str List To Obds)
数据结构·list·剪映小助手·视频剪辑自动化·剪映api·开源剪映小助手
阳明山水2 小时前
销量预测2026下半年:新架构、新基准与新共识
人工智能·深度学习·算法·机器学习·架构
知识分享小能手2 小时前
高等数学学习教程,从入门到精通,定积分 — 知识点全面总结(9)
学习·算法·机器学习
noipp2 小时前
推荐题目:洛谷 P3726 [AHOI2017/HNOI2017] 抛硬币
c语言·数据结构·c++·算法·编程·洛谷·luogu
c238562 小时前
C/C++每日一练14
c语言·c++·算法
程序员-珍2 小时前
力扣 877. 石子游戏
算法·leetcode·游戏
Kina_C2 小时前
HAProxy 负载均衡算法全解析:从静态轮询到一致性哈希的实战指南
linux·算法·负载均衡·哈希算法·haproxy