2023河南萌新联赛第(一)场:河南农业大学 11/12
晚来了一小时,终榜14名,血亏
https://ac.nowcoder.com/acm/contest/61132
A题不会,我选择oeis
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
n=int(input()) print(n*(n+1)*(n+2)//6%1000000007)
B题考虑线段树f[x][i][0]表示如果x所统辖的区间里,x第i位为0做计算得到的值,f[x][i][1]表示x所统辖的区间里,第i位为1做计算得到的值
那么叶子节点可以写三个if,合并两个区间时可以使用
f[x][i][0]=f[x*2+1][i][f[x*2][i][0]!=0];
f[x][i][1]=f[x*2+1][i][f[x*2][i][1]!=0];
询问时可以把[l,r]区间的转移矩阵合并,最后对于x的每一位计算答案
合并操作;
for
(
int
i=0;i<=20;i++)
{
t[i][0]=f[x][i][t[i][0]!=0];
t[i][1]=f[x][i][t[i][1]!=0];
}
计算答案:
B
D
E
F
G
H
I
J
K
L
for
(
int
i=0;i<=20;i++)
{
ans=ans+t[i][ (x&(1<<i))!=0] ;
}
(太C了,写了十五分钟就过了)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n; char s[100010]; int f[400010][25][2],t[25][2],a[100010]; int work(int xx,char c,int yy) { if(c=='|') return xx|yy; else if(c=='^') return xx^yy; else return xx&yy; } void build(int x,int l,int r) { if(l==r) { for(int i=0;i<=20;i++) { f[x][i][0]=work(0,s[l],a[l]&(1<<i)); f[x][i][1]=work(1<<i,s[l],a[l]&(1<<i)); } return ; } int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); for(int i=0;i<=20;i++) { f[x][i][0]=f[x*2+1][i][f[x*2][i][0]!=0]; f[x][i][1]=f[x*2+1][i][f[x*2][i][1]!=0]; } } void add(int x,int l,int r,int d) { if(l==r) { for(int i=0;i<=20;i++) { f[x][i][0]=work(0,s[l],a[l]&(1<<i)); f[x][i][1]=work(1<<i,s[l],a[l]&(1<<i)); } return ; } int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d); else add(x*2+1,mid+1,r,d); for(int i=0;i<=20;i++) { f[x][i][0]=f[x*2+1][i][f[x*2][i][0]!=0]; f[x][i][1]=f[x*2+1][i][f[x*2][i][1]!=0]; } } void ask(int x,int l,int r,int tl,int tr ) { if(tl<=l&&r<=tr) { for(int i=0;i<=20;i++) { t[i][0]=f[x][i][t[i][0]!=0]; t[i][1]=f[x][i][t[i][1]!=0]; } return ; } int mid=(l+r)/2; if(tl<=mid) ask(x*2,l,mid,tl,tr); if(tr>mid) ask(x*2+1,mid+1,r,tl,tr); } int main() { // freopen("1.in","r",stdin); n=read(); scanf("%s",s+1); for(int i=1;i<=n;i++) a[i]=read(); build(1,1,n); for(int m=read();m;m--) { if(read()&1) { int pos=read(); a[pos]=read(); add(1,1,n,pos); } else { int x=read(),l=read(),r=read(),ans=0; for(int i=0;i<=20;i++) { t[i][0]=0; t[i][1]=(1<<i); } ask(1,1,n,l,r); for(int i=0;i<=20;i++) { ans=ans+t[i][ (x&(1<<i))!=0] ; } printf("%d\n",ans); } } }
C
如果先手一次能赢,输出alice
如果先手第一次怎么动,后手第一次一定能赢,输出Bob
否则输出平局
D
考虑二分答案,问题转变成判断,只经过小于等于val的点,到达ed的最短路是否小于等于h,暴力跑一跑即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n,m,st,ed,a[10010],vis[10010],h; ll d[10010]; queue<int>q; vector<int>e[10010],v[10010]; int check(int val)//要求路上的松果数量<=val { memset(d,0x3f,sizeof(d)); if(a[st]>val) return 0; d[st]=0; q.push(st); while(q.size()) { int tx=q.front(); q.pop(); vis[tx]=0; for(int i=0;i<e[tx].size();i++) { if(a[e[tx][i]]<=val&&d[tx]+v[tx][i]<d[e[tx][i]]) { d[e[tx][i]]=d[tx]+v[tx][i]; if(vis[e[tx][i]]==0) { vis[e[tx][i]]=1; q.push(e[tx][i]); } } } } if(d[ed]<=h) return 1; return 0; } int main() { // freopen("1.in","r",stdin); n=read();m=read();st=read();ed=read();h=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); e[x].push_back(y); v[x].push_back(z); e[y].push_back(x); v[y].push_back(z); } int l=1,r=10000000,mid; while(l+1<r) { mid=(l+r)/2; if(check(mid)) r=mid; else l=mid; } if(check(l)) cout<<l; else if(check(r)) cout<<r; else cout<<-1; }
E
对于当前前缀和sum,只需判断sum-m是否出现过即可,用前缀和维护一下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n,m,ans; ll sum; map<ll,int>o; int main() { n=read();m=read(); o[0]=1; for(int i=1;i<=n;i++) { sum=sum+read(); ans=ans+o[sum-m]; o[sum]++; } cout<<ans; }
F
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int ans; int n,a[100010],fa[100010]; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } void merge(int x,int y) { if(get(x)==get(y)) return ; fa[get(x)]=get(y); } int main() { n=read(); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++) { a[i]=read(); merge(a[i],i); } ans=n; for(int i=1;i<=n;i++) if(i==get(i)) ans--; cout<<ans; }
G
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
如果原来的串没有连续为1的段,那么当然输出0 只有一个连续为1的段,那就输出这个串的长度 否则,如果有多个连续为1的串,可以把最长的两串转到一块,这样一定是最大的 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n,sum; char s[1000010]; vector<int>a; int main() { n=read(); scanf("%s",s+1); for(int i=1;i<=n;i++) { if(s[i]=='1') sum++; else { a.push_back(sum); sum=0; } } if(sum) a.push_back(sum); sort(a.begin(),a.end()); if(a.size()==0) cout<<0; if(a.size()==1) printf("%d",a[0]); else printf("%d",a[a.size()-1]+a[a.size()-2]); }
H
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
01bfs板子题 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n,m,d[3010][3010],len[3010][3010]; char s[3010][3010]; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; deque<pair<int,int>>q; int main() { n=read();m=read(); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=read();i;i--) { int x=read(),y=read(); len[x][y]=read(); } memset(d,0x3f,sizeof(d)); d[1][1]=0; q.push_front({1,1}); while(q.size()) { int x=q.front().first,y=q.front().second; int tx,ty; q.pop_front(); if(s[x][y]=='.') for(int i=0;i<4;i++) { tx=x+dx[i]; ty=y+dy[i]; if(tx&&ty&&tx<=n&&ty<=m&&s[tx][ty]!='#'&&d[tx][ty]>d[x][y]+1) { d[tx][ty]=d[x][y]+1; q.push_back({tx,ty}); } } else for(int i=0;i<4;i++) { tx=x+dx[i]*len[x][y]; ty=y+dy[i]*len[x][y]; if(tx>=1&&ty>=1&&tx<=n&&ty<=m&&s[tx][ty]!='#'&&d[tx][ty]>d[x][y]) { d[tx][ty]=d[x][y]; q.push_front({tx,ty}); } } } if(d[n][m]==0x3f3f3f3f) d[n][m]=-1; cout<<d[n][m]; }
I
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
lca+树上差分,wa了半天,17分钟过样例交了,一小时后才过,自闭极了 考虑在x点到y点的简单路径上所有的边都增加add个松果 这个操作,是链上加,可以对x加add,对y+add,对lca(x,y)减2倍的add 然后从差分数组整一下原数组 每次询问lca一下即可。 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n,m,q,fa[100010][25],deep[100010]; ll d[100010][25],v[100010]; vector<pair<int,ll>>e[100010]; void dfs(int x) { for(auto y:e[x]) { if(y.first==fa[x][0])continue; deep[y.first]=deep[x]+1; fa[y.first][0]=x; dfs(y.first); } } int lca(int x,int y) { if(deep[x]>deep[y])swap(x,y); for(int i=20;i>=0;i--) if(deep[fa[y][i]]>=deep[x]) y=fa[y][i]; if(x==y) return x; for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } ll ask(int x,int y) { ll t=0; if(deep[x]>deep[y])swap(x,y); for(int i=20;i>=0;i--) if(deep[fa[y][i]]>=deep[x]) { t=t+d[y][i]; y=fa[y][i]; } if(x==y) return t; for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]){ t=t+d[x][i]; t=t+d[y][i]; x=fa[x][i],y=fa[y][i]; } return t+d[x][0]+d[y][0]; } ll dfs0(int x) { ll sum=v[x]; for(auto y:e[x]) { if(y.first==fa[x][0])continue; ll t=dfs0(y.first); sum+=t; d[y.first][0]=y.second+t; } return sum; } int main() { // freopen("1.in","r",stdin); n=read();m=read();q=read(); for(int i=1;i<n;i++) { int x=read(),y=read(),v=read(); e[x].push_back({y,v}); e[y].push_back({x,v}); } deep[1]=1; dfs(1); for(int i=1;i<=20;i++) for(int x=1;x<=n;x++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=1;i<=m;i++) { int x=read(),y=read(),tv=read(); v[x]+=tv; v[y]+=tv; v[lca(x,y)]-=2*tv; } dfs0(1); for(int i=1;i<=20;i++) for(int x=1;x<=n;x++) d[x][i]=d[x][i-1]+d[fa[x][i-1]][i-1]; for(int i=1;i<=q;i++) { int x=read(),y=read(); printf("%lld\n",ask(x,y)); } }
J
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
简单签到题 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int a[1010],n,sum; int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); a[0]=1; sort(a+0,a+1+n); for(int i=1;i<n;i++) sum=sum+a[i]; printf("%.6lf ",sum*1.0/(n-1)); a[0]=100;sum=0; sort(a+0,a+1+n); for(int i=1;i<n;i++) sum=sum+a[i]; printf("%.6lf",sum*1.0/(n-1)); }
K
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
模拟,对于每个房间,判断一下上下左右 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n,m,ans; char s[1010][1010]; int main() { n=read();m=read(); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(s[i][j]!='0')continue; int sum=0; if(s[i][j+1]=='2'||s[i][j-1]=='2'||s[i+1][j]=='2'||s[i-1][j]=='2') continue; sum=sum+(s[i][j+1]=='1'); sum=sum+(s[i][j-1]=='1'); sum=sum+(s[i-1][j]=='1'); sum=sum+(s[i+1][j]=='1'); if(sum==3) ans++; } } if(ans) cout<<"YES"<<endl<<ans; else cout<<"NO"; }
L
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
考虑用值域线段树维护当前的数组,查询中位数可以使用线段树上二分 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int c[4000010],n,a[1000010],m; void add(int x,int l,int r,int d,int v) { c[x]+=v; if(l==r) return ; int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d,v); else add(x*2+1,mid+1,r,d,v); } int ask(int x,int l,int r,int sum) { // cout<<x<<" "<<l<<' '<<r<<' '<<c[x]<<" "<<sum<<endl; if(l==r) return l; int mid=(l+r)/2; if(c[x*2]>=sum) return ask(x*2,l,mid,sum); else return ask(x*2+1,mid+1,r,sum-c[x*2]); } int main() { n=read();m=read(); for(int i=1;i<=n;i++) { a[i]=read(); add(1,1,1000000,a[i],1); } n=n/2+1; for(int i=1;i<=m;i++) { int p=read(),x=read(); add(1,1,1000000,a[p],-1); a[p]=x; add(1,1,1000000,x,1); printf("%d\n",ask(1,1,1000000,n)); } }