牛客练习赛 108 题解
六道题目的出题人都是我,希望大家玩的开心!
https://ac.nowcoder.com/acm/contest/51208
A. 惊鸿
显然位或之后只会变大,因此答案为 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
signed main(void){
int tt=read();while(tt--){
int a=read(),b=read(),c=read(),d=read();
cout<<((a|b|c|d)<<2)<<endl;
}
return 0;
}
B. 风间
有解必须要 ,且 。
那么首先判掉无解,如果有解,记 ,答案就是
略证:考虑每个 ,算出 之间转移了多少次。当 时,左侧有 这么多值要转到右边去,每次最多转移 ,因此贡献至少为 ; 时同理。显然可以构造出方案。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
const int N=2e5+5;
int a[N],b[N],n;
void solve(){
n=read();int ans=0,A=0,B=0;
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
for(int i=1;i<=n;i++){
if(a[i]%2!=b[i]%2){puts("-1");return ;}
A+=a[i],B+=b[i],ans+=(max(A,B)-min(A,B))>>1;
}
if(A!=B){puts("-1");return ;}
cout<<ans<<endl;
}
signed main(void){
int tt=read();
while(tt--)solve();
return 0;
}
C. 梦迹
考虑开一个桶,记 为满足 的 的个数,再设其前缀和 。
如果不考虑 的限制,那么答案就是
一次修改相当于将某个 。考虑 对答案的影响。
分两种情况讨论:
- :这种情况比较简单, 式的第 项与第 项发生了改变。对于 , 变为了 ,增加了 ;对于第 项, 变为 ,增加了 。因此只需将答案加上 ,再处理对应的 的改动即可。
- :此时 式的第 项发生改变,但第 项的改变不同。对于 且 ,这一项的增加仍为 ;但对于第 项,由 变成了 ,增加了 ,因此总的增幅为 。
使用树状数组维护单点加与前缀和,复杂度 ,其中 为值域。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
const int N=3e5+5;
int n,q,W,a[N],cnt,ans;
struct BIT{
int c[N];
int lowbit(int x){return x&(-x);}
void add(int x,int k){x++;for(int i=x;i<=W+1;i+=lowbit(i))c[i]+=k;}
int sum(int x,int res=0){x++;for(int i=x;i;i-=lowbit(i))res+=c[i];return res;}
}T;
void Modify(int x,int k){
cnt+=k*(x+x<=W);
if(x+x>W)ans+=k*2*T.sum(W-x),T.add(x,k);
else ans+=k*2*T.sum(W-x)+1,T.add(x,k);
}
signed main(void){
n=read(),q=read(),W=read();
for(int i=1;i<=n;i++)a[i]=read(),Modify(a[i],1);
for(int i=1;i<=q;i++){
int p=read(),x=read();Modify(a[p],-1),Modify(x,1),a[p]=x;
cout<<(ans+cnt)/2<<endl;
}
return 0;
}
D. Small Cloud Sugar Candy
BV1Uy4y137SA
对每条限制连边,那么得到一张无向图。考虑每个连通块,
- 若存在奇环,那么奇环上的点权都可以直接确定,进而确定整个连通块,看看是否有矛盾即可。具体来说,如果我们找到了一个奇环,设其上的边权分别为 ,其中 () 连接 , 连接 ,那么 。
- 否则,我们随便钦定一个点,让它的权值为 (或者任意数),那么整个连通块的值都可以确定。确定后看是否有矛盾即可。
时间复杂度 。显然构造出的序列每一项都在 中。
Bonus:
- 实际上我们也可以构造出满足 仍然在 内的解
- 并且能构造字典序最小的解,还能输出方案数。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
const int N=5e5+5;
struct Edge{
int cost,to;
Edge(int T,int C):to(T),cost(C){}
Edge(){}
};
vector<Edge>G[N];
int dep[N],fa[N],val[N],a[N],fir;
bool chk=0,vis[N],calc[N];
bool Ans=1;
void dfs1(int u){
vis[u]=1;
for(auto t:G[u]){
int v=t.to,w=t.cost;
if(v==fa[u])continue;
else if(!vis[v]){dep[v]=dep[u]+1,fa[v]=u,val[v]=w,dfs1(v);continue;}
if((!chk)&&(dep[u]-dep[v]+1)%2==1){
int p=u,now=1,sum=w;
while(p!=v)sum+=val[p]*now,now=-now,p=fa[p];
if(sum%2!=0)Ans=0;
a[u]=sum/2,fir=u,chk=1,calc[u]=1;
}
}
}
void dfs2(int u){
calc[u]=1;
for(auto t:G[u]){
int v=t.to,w=t.cost;
if(!calc[v])a[v]=w-a[u],dfs2(v);
else if(calc[v]&&a[u]+a[v]!=w)Ans=0;
}
}
int n,m,fr[N],to[N],we[N];
void solve(){
n=read(),m=read();Ans=1;
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();fr[i]=u,to[i]=v,we[i]=w;
G[u].push_back(Edge(v,w)),G[v].push_back(Edge(u,w));
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
chk=0,fir=i,dfs1(i),dfs2(fir);
}
if(!Ans)puts("-1");
else{for(int i=1;i<=n;i++)cout<<a[i]<<" ";puts("");}
for(int i=1;i<=n;i++)dep[i]=fa[i]=val[i]=a[i]=calc[i]=vis[i]=0;fir=chk=0;
for(int i=1;i<=n;i++)G[i].clear();
}
signed main(void){
int tt=read();while(tt--)solve();
return 0;
}
E. 琉焰
对于每次询问,设 为此时连通块数量, 为此时的边数,那么答案就是 。
为什么呢,首先可以对每个连通块单独考虑。随便拎出来一个生成树,那么合法的方案与非树边的非空子集一一对应。一个大致的证明如下:
-
对于任意一个非树边的非空子集,我们考虑这样构造方案:对于这个子集中的每条边 ,将森林中 的路径上的所有边以及 这条边覆盖一次,那么最终我们选取的边集就是所有覆盖次数为奇数的边组成的集合。不难验证这个生成子图确实符合条件。
-
考虑一个符合条件的生成子图,那么图中的每个连通块都存在一条欧拉回路。欧拉回路一定可以被拆分成若干个环的并,而在上面的构造方式中,每个环的刻画方式是唯一的。因此两边就一一对应了。
一个连通块的非树边数量是 边数 - 点数 +1,当有 个连通块时,方案数就是 ,减一是减去空集。
考虑线段树分治,把 的存在区间拆成 个小区间插入线段树,然后 DFS 整棵树并处理对应的修改即可。
使用可撤销并查集维护连通块个数,时间复杂度不会超过 。
//我是宵宫小姐的狗
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
const int mod=998244353;
int ksm(int x,int y,int p=mod){
int ans=1;
for(int i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p;
return ans%p;
}
int inv(int x,int p=mod){return ksm(x,p-2,p)%p;}
int randint(int l,int r){return rand()*rand()%(r-l+1)+l;}
#define fi first
#define se second
#define mk make_pair
const int N=2e5+5;
int n,m;
map<pair<int,int>,int>Map;
int fa[N],sz[N],top=0,cnt=0,P[N];
pair<int,int>stk[N];
int find(int x){return x==fa[x]?x:find(fa[x]);}
void adde(int x,int y){
x=find(x),y=find(y);
if(x==y)return ;
if(sz[x]>sz[y])swap(x,y);
sz[y]+=sz[x],fa[x]=y,stk[++top]=mk(x,y),cnt--;
}
void undo(int id){
int x=stk[id].fi,y=stk[id].se;
sz[y]-=sz[x],fa[x]=x,cnt++;
}
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
vector<pair<int,int> >vec[N<<2];
void add(int l,int r,pair<int,int>k,int ql,int qr,int p){
if(l<=ql&&qr<=r){vec[p].emplace_back(k);return ;}
int mid=(ql+qr)>>1;
if(l<=mid)add(l,r,k,ql,mid,ls(p));
if(r>mid)add(l,r,k,mid+1,qr,rs(p));
}
int Ans[N],cur[N];
void dfs(int ql,int qr,int p){
int now=top;for(auto t:vec[p])adde(t.fi,t.se);
if(ql==qr){Ans[ql]=P[cur[ql]-n+cnt]-1;while(top>now)undo(top--);return ;}
int mid=(ql+qr)>>1;dfs(ql,mid,ls(p)),dfs(mid+1,qr,rs(p));
while(top>now)undo(top--);
}
int st[N],ed[N],fr[N],to[N];
signed main(void){
n=read(),m=read();int tot=0;
for(int i=1;i<=m;i++){
int u=read(),v=read();auto it=Map.find(mk(u,v));
if(it==Map.end())Map[mk(u,v)]=i,cur[i]=cur[i-1]+1;
else st[++tot]=it->se,ed[tot]=i-1,fr[tot]=u,to[tot]=v,Map.erase(it),cur[i]=cur[i-1]-1;
}
for(auto it:Map)st[++tot]=it.se,ed[tot]=m,fr[tot]=it.fi.fi,to[tot]=it.fi.se;
P[0]=1;for(int i=1;i<=m;i++)P[i]=(P[i-1]<<1)%mod;
for(int i=1;i<=n;i++)fa[i]=i,sz[i]=1;cnt=n;
for(int i=1;i<=tot;i++)add(st[i],ed[i],mk(fr[i],to[i]),1,m,1);
dfs(1,m,1);for(int i=1;i<=m;i++)cout<<Ans[i]<<'\n';
return 0;
}
F. wavedash.ppt
首先我们把平方拆开,有
考虑交换求和顺序,枚举 ,有
注意到 ,因此上式即
我们定义 ,则只需要求
考虑设 表示 中拓扑序最小的点为 (这里认为存在连边 则 的拓扑序小于 ),且只计入从 开始的路径 ,但不计入 的贡献,此时的所有 的贡献之和;同时预处理 表示所有从 开始到 结束的路径 ,但不计入 的贡献的 之和,那么有转移
同时定义
并类似地定义 ,则初值为 ,答案即为 。
拓扑排序后 DP 即可。时间复杂度为 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
const int mod=998244353;
int ksm(int x,int y,int p=mod){
int ans=1;
for(int i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p;
return ans%p;
}
int inv(int x,int p=mod){return ksm(x,p-2,p)%p;}
int randint(int l,int r){return rand()*rand()%(r-l+1)+l;}
void add(int &x,int v){x+=v;if(x>=mod)x-=mod;}
const int N=4005;
const int iv=inv(2);
int f[N][N],g[N],n,m,w[N],ind[N];
vector<int>G[N],tp;
queue<int>q;
bool topo(){
for(int i=1;i<=n;i++)if(!ind[i])q.push(i);
while(q.size()){
int x=q.front();q.pop();tp.emplace_back(x);
for(int v:G[x])if(--ind[v]==0)q.push(v);
}
return (tp.size()==n+1);
}
signed main(void){
n=read(),m=read();
for(int i=1;i<=n;i++)w[i]=read()*iv%mod;
for(int i=1;i<=m;i++){int u=read(),v=read();G[u].emplace_back(v),ind[v]++;}
tp.emplace_back(0);assert(topo());tp.emplace_back(n+1);
for(int i=1;i<=n;i++){
int u=tp[i];f[u][u]=1;
for(int j=i;j<=n;j++){
int v=tp[j];for(int x:G[v])add(f[u][x],f[u][v]*w[x]%mod);
}
}
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)add(f[0][i],f[j][i]*w[j]%mod),add(f[i][n+1],f[i][j]);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)add(f[0][n+1],f[i][j]*w[i]%mod);
g[n+1]=1;for(int i=n;i>=0;i--){
for(int j=i+1;j<=n+1;j++)add(g[tp[i]],g[tp[j]]*f[tp[i]][tp[j]]%mod*f[tp[i]][tp[j]]%mod);
}
cout<<g[0]*ksm(2,n)%mod<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?