百度之星2023决赛简要题解
前言
本人考试前一直在学文化,疏于训练,再加上考试时感冒头晕发挥不佳,只切了 ABCDH ,成功打铁。看来该加训了!
A 找矩阵
对每个点算出上下左右能到达的最远点,直接枚举 S 在方框的哪一条边然后枚举对边直接判断就好了。有一些细节需要注意一下。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
I love Elaina;
const int N=3010;
int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,L[N][N],R[N][N],U[N][N],D[N][N],l1,r1,l2,r2,tl,tr;
char mp[N][N],tmp[N][N];
pair<int,int>pos;
void solve(){
memset(L,0x3f,sizeof(L)),memset(R,-1,sizeof(R)),memset(U,0x3f,sizeof(U)),memset(D,-1,sizeof(D));
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(mp[i][j]=='S')pos=make_pair(i,j);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]=='#')continue;
L[i][j]=min(j,L[i][j-1]);
}
for(int j=m;j;j--){
if(mp[i][j]=='#')continue;
R[i][j]=max(j,R[i][j+1]);
}
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
if(mp[i][j]=='#')continue;
U[i][j]=min(i,U[i-1][j]);
}
for(int i=n;i;i--){
if(mp[i][j]=='#')continue;
D[i][j]=max(i,D[i+1][j]);
}
}
for(int i=pos.first;i<=pos.first;i++){
for(int j=pos.first+1;j<=n;j++){
if(mp[j][pos.second]=='#')continue;
l1=L[i][pos.second],r1=R[i][pos.second],l2=L[j][pos.second],r2=R[j][pos.second],tl=tr=-1;
if(l1>=l2){
for(int k=l1;k<=pos.second;k++){
if(D[i][k]>=j){
tl=k;
break;
}
}
}
else{
for(int k=l2;k<=pos.second;k++){
if(U[j][k]<=i){
tl=k;
break;
}
}
}
if(r1<=r2){
for(int k=r1;k>=pos.second;k--){
if(D[i][k]>=j){
tr=k;
break;
}
}
}
else{
for(int k=r2;k>=pos.second;k--){
if(U[j][k]<=i){
tr=k;
break;
}
}
}
if(tl!=tr&&tl!=-1&&tr!=-1){
puts("Yes");
exit(0);
}
}
}
for(int i=1;i<pos.first;i++){
for(int j=pos.first;j<=pos.first;j++){
if(mp[i][pos.second]=='#')continue;
l1=L[i][pos.second],r1=R[i][pos.second],l2=L[j][pos.second],r2=R[j][pos.second],tl=tr=-1;
if(l1>=l2){
for(int k=l1;k<=pos.second;k++){
if(D[i][k]>=j){
tl=k;
break;
}
}
}
else{
for(int k=l2;k<=pos.second;k++){
if(U[j][k]<=i){
tl=k;
break;
}
}
}
if(r1<=r2){
for(int k=r1;k>=pos.second;k--){
if(D[i][k]>=j){
tr=k;
break;
}
}
}
else{
for(int k=r2;k>=pos.second;k--){
if(U[j][k]<=i){
tr=k;
break;
}
}
}
if(tl!=tr&&tl!=-1&&tr!=-1){
puts("Yes");
exit(0);
}
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);
solve();
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)tmp[i][j]=mp[i][j];
swap(n,m);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)mp[i][j]=tmp[j][i];
solve();
puts("No");
return 0;
}
B 错峰旅行
考虑离散化,用差分算出每一段不拥挤城市个数即可。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=5010;
const int M=1000010;
const ll mod=1e9+7;
int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,s,t,k,x,L[M],R[M],pos[M<<1],cur,p,qwq,d[M<<1];
ll ans=1;
int main(){
n=read(),m=read(),s=read(),t=read()+1;
for(int i=1;i<=m;i++)x=read(),L[i]=pos[2*i-1]=read(),R[i]=pos[2*i]=read()+1;
pos[2*m+1]=s,pos[2*m+2]=t;
sort(pos+1,pos+2*m+3);
k=unique(pos+1,pos+2*m+3)-pos-1;
s=lower_bound(pos+1,pos+k+1,s)-pos,t=lower_bound(pos+1,pos+k+1,t)-pos;
for(int i=1;i<=m;i++){
L[i]=lower_bound(pos+1,pos+k+1,L[i])-pos,R[i]=lower_bound(pos+1,pos+k+1,R[i])-pos;
d[L[i]]--,d[R[i]]++;
}
cur=n;
for(int i=1;i<t;i++){
cur+=d[i];
if(i>=s){
p=pos[i+1]-pos[i],qwq=cur;
while(p){
if(p&1)ans=1ll*ans*qwq%mod;
qwq=1ll*qwq*qwq%mod,p>>=1;
}
}
}
printf("%lld",ans);
return 0;
}
C 社交树
考虑将 \(a^{x+d}\) 拆成 \(a^{x-dep_u}\) 和 \(a^{dep_v}\) ,把 \(a^{x-dep_u}\) 放线段树里面存,然后查询的时候再乘上 \(a^{dep_v}\) 就可以了。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=1e5+10;
const ll mod=1e8+7;
const int block=10000;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,m,A,op,x,y,head[N],cnt,siz[N],dfn[N],pos[N],p1[N],p2[N],idx,ans[N],dep[N];
struct edge{int to,nxt;}e[N<<1];
void addedge(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
ll qp(int p){return 1ll*p2[p/block]*p1[p%block]%mod;}
void dfs(int u,int fa){
siz[u]=1,dfn[u]=++idx,pos[idx]=u,dep[u]=dep[fa]+1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
namespace AyaseEli{
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
struct node{
int l,r;
ll val;
}tree[N<<2];
void pushdown(node *tree,int k){
if(tree[k].val){
tree[ls(k)].val=(tree[ls(k)].val+tree[k].val)%mod,tree[rs(k)].val=(tree[rs(k)].val+tree[k].val)%mod;
tree[k].val=0;
}
}
void build(node *tree,int k,int l,int r){
tree[k].l=l,tree[k].r=r;
if(l==r)return;
int mid=(l+r)/2;
build(tree,ls(k),l,mid),build(tree,rs(k),mid+1,r);
}
void change(node *tree,int k,int ql,int qr,ll v){
if(ql<=tree[k].l&&tree[k].r<=qr){
tree[k].val=(tree[k].val+v)%mod;
return;
}
pushdown(tree,k);
int mid=(tree[k].l+tree[k].r)/2;
if(ql<=mid)change(tree,ls(k),ql,qr,v);
if(mid<qr)change(tree,rs(k),ql,qr,v);
}
ll query(node *tree,int k,int q){
if(tree[k].l==tree[k].r)return tree[k].val;
pushdown(tree,k);
int mid=(tree[k].l+tree[k].r)/2;
if(q<=mid)return query(tree,ls(k),q);
else return query(tree,rs(k),q);
}
}
I love AyaseEli;
int main(){
n=read(),m=read(),A=(read()+mod)%mod;
p1[0]=p2[0]=1;
for(int i=1;i<=block;i++)p1[i]=1ll*p1[i-1]*A%mod;
for(int i=1;i<=block;i++)p2[i]=1ll*p2[i-1]*p1[block]%mod;
for(int i=2;i<=n;i++){
x=read();
addedge(x,i),addedge(i,x);
}
dfs(1,0),build(tree,1,1,n);
while(m--){
op=read();
if(op==1){
x=read(),y=(read()+mod-1)%(mod-1);
change(tree,1,dfn[x],dfn[x]+siz[x]-1,qp((y-dep[x]+mod-1)%(mod-1)));
}
if(op==2){
x=read();
printf("%lld\n",1ll*query(tree,1,dfn[x])*qp(dep[x])%mod);
}
}
return 0;
}
D 传信游戏
反向连边,看看能从 0 走到哪些点就行了。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
I love Elaina;
const int N=1010;
int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,a[N],ans;
vector<int>G[N];
void dfs(int u){
ans^=u;
for(int v:G[u])dfs(v);
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
G[a[i]].push_back(i);
}
dfs(0);
printf("%d",ans);
return 0;
}
E 喵喵卫士,全靠你了
考虑按层数从深往浅 dp 。我们设 \(f_{i,j,k}\) 表示当前填到第 i 层,还有 j 个点没有填,第 i 层填了 k 个点的方案数。转移方程很容易推。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=110;
const ll mod=1e9+7;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,a[N],f[N][N][N],p[N],cnt[N],sum[N],fac[N],ifac[N],C[N][N];
ll qp(ll x,ll p){
ll res=1;
while(p){
if(p&1)res=1ll*res*x%mod;
x=1ll*x*x%mod,p>>=1;
}
return res;
}
int main(){
fac[0]=C[0][0]=1;
for(int i=1;i<=100;i++)fac[i]=1ll*fac[i-1]*i%mod;
ifac[100]=qp(fac[100],mod-2);
for(int i=100;i;i--)ifac[i-1]=1ll*ifac[i]*i%mod;
for(int i=1;i<=100;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
n=read();
for(int i=1;i<=n;i++)a[i]=read(),cnt[a[i]]++;
for(int i=n-1;i>=0;i--)sum[i]=sum[i+1]+cnt[i];
for(int i=0;i<=n;i++)f[i][sum[i]][0]=1;
for(int d=n-1;d>=0;d--)for(int i=0;i<=sum[d+1];i++)for(int j=0;j<=sum[d+1]-i;j++)for(int k=1;k<=cnt[d]+i;k++)f[d][i+cnt[d]-k][k]=(f[d][i+cnt[d]-k][k]+1ll*f[d+1][i][j]*fac[j+k-1]%mod*ifac[k-1]%mod*C[i+cnt[d]][k]%mod)%mod;
printf("%lld",f[0][0][1]);
return 0;
}
F 图论大师
直接求邻接矩阵的 k 次幂即可。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=110;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,m,q,x,y,z;
struct matrix1{
ll n,m,a[N][N];
matrix1(){memset(a,0x3f,sizeof(a));}
matrix1 operator*(const matrix1 &rhs)const{
matrix1 res;
res.n=n,res.m=rhs.m;
for(int k=1;k<=m;k++)for(int i=1;i<=n;i++)for(int j=1;j<=rhs.m;j++)res.a[i][j]=min(res.a[i][j],a[i][k]+rhs.a[k][j]);
return res;
}
}bit1[30],B1;
struct matrix2{
ll n,m,a[N][N];
matrix2(){memset(a,-0x3f,sizeof(a));}
matrix2 operator*(const matrix2 &rhs)const{
matrix2 res;
res.n=n,res.m=rhs.m;
for(int k=1;k<=m;k++)for(int i=1;i<=n;i++)for(int j=1;j<=rhs.m;j++)res.a[i][j]=max(res.a[i][j],a[i][k]+rhs.a[k][j]);
return res;
}
}bit2[30],B2;
int main(){
n=read(),m=read(),q=read();
bit1[0].n=bit1[0].m=bit2[0].n=bit2[0].m=B1.m=B2.m=n,B1.n=B2.n=1;
for(int i=1;i<=m;i++){
x=read(),y=read(),z=read();
bit1[0].a[x][y]=min(bit1[0].a[x][y],z),bit1[0].a[y][x]=bit1[0].a[x][y];
bit2[0].a[x][y]=max(bit2[0].a[x][y],z),bit2[0].a[y][x]=bit2[0].a[x][y];
}
for(int i=1;i<30;i++)bit1[i]=bit1[i-1]*bit1[i-1],bit2[i]=bit2[i-1]*bit2[i-1];
while(q--){
x=read(),y=read(),z=read();
for(int i=1;i<=n;i++)B1.a[1][i]=0x3f3f3f3f3f3f3f3f,B2.a[1][i]=-0x3f3f3f3f3f3f3f3f;
B1.a[1][x]=B2.a[1][x]=0;
for(int i=0;i<30;i++)if(z&(1<<i))B1=B1*bit1[i],B2=B2*bit2[i];
if(B2.a[1][y]<0)puts("-1 -1");
else printf("%lld %lld\n",B1.a[1][y],B2.a[1][y]);
}
return 0;
}
G 这一击贯穿星辰
可以先通过背包求出分配 x 点体力时 \(A\times B\) 的最大值,以及 \(C\) 的最大值,这样会对应出 \(m\) 种可能的最优方案。注意到对于每一种方案,都会得到一条关于 \(D\) 的一次函数,这样我们用李超线段树或者其他例如分治、凸包之类的方法就可以求解了。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=3010;
const int M=300010;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,q,x,y,z;
ll f[4][N];
vector<pair<int,int> >qwq[3];
namespace AyaseEli{
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
struct line{
ll k,b;
ll F(ll x){return 1ll*k*x+b;}
}tree[M<<2];
void modify(line *tree,int k,int l,int r,line v){
if(l==r){
if(tree[k].F(l)<v.F(l))tree[k]=v;
return;
}
int mid=(l+r)/2;
if(tree[k].F(mid)<v.F(mid))swap(tree[k],v);
if(tree[k].F(l)<v.F(l))modify(tree,ls(k),l,mid,v);
else modify(tree,rs(k),mid+1,r,v);
}
ll query(line *tree,int k,int l,int r,int q){
ll res=tree[k].F(q);
if(l==r)return tree[k].F(q);
int mid=(l+r)/2;
if(q<=mid)res=max(res,query(tree,ls(k),l,mid,q));
else res=max(res,query(tree,rs(k),mid+1,r,q));
return res;
}
}
I love AyaseEli;
int main(){
n=read(),m=read(),q=read();
for(int i=1;i<=n;i++){
x=read(),y=read()-1,z=read();
qwq[y].push_back(make_pair(x,z));
}
for(int i=0;i<3;i++)for(pair<int,int>tmp:qwq[i])for(int j=m;j>=tmp.first;j--)f[i][j]=max(f[i][j],f[i][j-tmp.first]+tmp.second);
for(int i=0;i<=m;i++)for(int j=0;j<=m-i;j++)f[3][i+j]=max(f[3][i+j],1ll*(100+f[0][i])*(100+f[1][j]));
for(int i=0;i<=m;i++)modify(tree,1,0,3e5,(line){-f[3][i],1ll*f[3][i]*(100+f[2][m-i])});
while(q--){printf("%lld\n",query(tree,1,0,3e5,read()));}
return 0;
}
H 小度的双色球
看上去就很根号分治。对于小集合可以直接暴力使用 set 进行求解。对于大集合我们先预处理,先求出另外的集合使得里面的数出现次数都是超过枚举阈值的,然后直接对求出的新集合暴力枚举就行,复杂度可以通过计算不等式证明。调整块长,可以使得复杂度变成 \(O(n\sqrt{nlogn})\) ,如果对小集合使用一些哈希表之类的东西可以去掉 log 。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
I love Elaina;
const int N=200010;
const int block=150;
const int mod=998244353;
int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,x,y,cnt,inv[N],ans[N<<4],id[N],len;
vector<int>rev[N],ts[N],qwq;
set<int>st[N];
int calc(int i,int j){return id[i]*len+id[j];}
int main(){
n=read(),m=read();
inv[1]=n;
for(int i=2;i<=200000;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++){
x=read();
while(x--){
y=read();
st[i].insert(y),rev[y].push_back(i);
}
}
for(int i=1;i<=n;i++){
if(rev[i].size()>block){
qwq.push_back(i);
for(int tmp:rev[i])ts[tmp].push_back(i);
}
}
sort(qwq.begin(),qwq.end());
len=qwq.size();
for(int i=0;i<qwq.size();i++)id[qwq[i]]=i;
for(int i=1;i<=n;i++){
if(ts[i].size()<2)continue;
for(int j=0;j<ts[i].size()-1;j++)for(int k=j+1;k<ts[i].size();k++)ans[calc(ts[i][j],ts[i][k])]++;
}
while(m--){
x=read(),y=read();
if(rev[x].size()>rev[y].size())swap(x,y);
cnt=0;
if(rev[x].size()<=block){for(int tmp:rev[x])if(*st[tmp].lower_bound(y)==y)cnt++;}
else{
if(x>y)swap(x,y);
cnt=ans[calc(x,y)];
}
if(!cnt)puts("-1");
else printf("%d\n",inv[cnt]);
}
return 0;
}
I 寻宝
二分答案,考虑网络流。感觉这题其实难点在想到用网络流,建模反而不难。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=510;
const int M=5010;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,m,p,a[N],b[N],c[N],d[N],x,y,l,r,mid,ans;
bool vis[N][N];
namespace AyaseEli{
int s,t,head[N<<2],cnt=1,dep[N<<2];
struct edge{
int to,nxt;
ll flow;
}e[M<<2];
void addedge(int u,int v,ll f){
e[++cnt].to=v,e[cnt].nxt=head[u],e[cnt].flow=f,head[u]=cnt;
e[++cnt].to=u,e[cnt].nxt=head[v],e[cnt].flow=0,head[v]=cnt;
}
bool bfs(){
memset(dep,-1,sizeof(dep));
queue<int>q;
q.push(s);
dep[s]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dep[v]==-1&&e[i].flow>0){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=-1;
}
ll dfs(int u,ll cur){
if(u==t||!cur)return cur;
ll tmp,sum=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].flow>0&&dep[u]+1==dep[v]){
tmp=dfs(v,min(cur-sum,e[i].flow));
if(!tmp)dep[v]=-1;
e[i].flow-=tmp,e[i^1].flow+=tmp,sum+=tmp;
if(sum==cur)break;
}
}
return sum;
}
ll Dinic(){
ll maxflow=0;
while(bfs())maxflow+=dfs(s,1e18);
return maxflow;
}
}
I love AyaseEli;
bool check(ll lim){
cnt=1;
memset(head,0,sizeof(head));
for(int i=1;i<=n;i++){
if(d[i]>mid){
addedge(i,i+n,b[i]),addedge(i+n,i+2*n,a[i]);
for(int j=1;j<=n;j++){
if(i==j||!vis[i][j])continue;
if(c[j]+d[j]<=mid)addedge(i+n,j+n,1e9);
}
}
else if(c[i]+d[i]>mid)addedge(i,i+n,b[i]),addedge(i+n,i+2*n,a[i]);
else addedge(i,i+n,0),addedge(i+n,i+2*n,a[i]);
addedge(s,i,1e9),addedge(i+2*n,t,1e9);
}
return Dinic()<=p;
}
int main(){
n=read(),m=read(),p=read();
s=0,t=3*n+1;
for(int i=1;i<=n;i++)a[i]=read(),b[i]=read(),c[i]=read(),d[i]=read();
for(int i=1;i<=m;i++){
x=read(),y=read();
vis[x][y]=vis[y][x]=1;
}
l=0,r=ans=2e9;
while(l<=r){
mid=(l+r)/2;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
printf("%lld",ans);
return 0;
}
J 石子游戏
自行模拟容易看出这样一个结论:最大值个数为奇数时先手获胜。于是我们可以考虑用线段树维护。细节比较多,线段树可以考虑离散化后建普通线段树,也可以考虑建值域线段树。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=500010;
const ll mod=998244353;
const ll inv2=499122177;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,m,op[N],x[N],y[N],pos[N];
ll qp(ll x,ll p){
ll res=1;
while(p){
if(p&1)res=1ll*res*x%mod;
x=1ll*x*x%mod,p>>=1;
}
return res;
}
namespace AyaseEli{
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
struct node{
int l,r;
ll sum,val,cnt;
}tree[N<<2];
void pushup(node *tree,int k){tree[k].sum=(1ll*tree[ls(k)].sum*tree[rs(k)].val%mod+tree[rs(k)].sum)%mod,tree[k].val=1ll*tree[ls(k)].val*tree[rs(k)].val%mod,tree[k].cnt=tree[ls(k)].cnt+tree[rs(k)].cnt;}
void build(node *tree,int k,int l,int r){
tree[k].l=l,tree[k].r=r,tree[k].val=1;
if(l==r)return;
int mid=(l+r)/2;
build(tree,ls(k),l,mid),build(tree,rs(k),mid+1,r);
pushup(tree,k);
}
void modify(node *tree,int k,int q,ll v){
if(tree[k].l==tree[k].r){
if(!tree[k].cnt)tree[k].cnt=1,tree[k].sum=inv2;
tree[k].val=1ll*tree[k].val*qp(inv2,v)%mod;
return;
}
int mid=(tree[k].l+tree[k].r)/2;
if(q<=mid)modify(tree,ls(k),q,v);
else modify(tree,rs(k),q,v);
pushup(tree,k);
}
pair<ll,ll>query(node *tree,int k,int ql,int qr){
pair<ll,ll>res,t1,t2;
if(ql<=tree[k].l&&tree[k].r<=qr)return make_pair(tree[k].sum,tree[k].val);
int mid=(tree[k].l+tree[k].r)/2;
if(qr<=mid)return query(tree,ls(k),ql,qr);
if(mid<ql)return query(tree,rs(k),ql,qr);
t1=query(tree,ls(k),ql,qr),t2=query(tree,rs(k),ql,qr);
res.first=(1ll*t1.first*t2.second%mod+t2.first)%mod,res.second=1ll*t1.second*t2.second%mod;
return res;
}
}
I love AyaseEli;
int main(){
n=read();
for(int i=1;i<=n;i++){
op[i]=read(),x[i]=read(),y[i]=read();
if(op[i]==1)pos[++m]=x[i];
}
sort(pos+1,pos+m+1);
m=unique(pos+1,pos+m+1)-pos-1;
build(tree,1,1,m);
for(int i=1;i<=n;i++){
if(op[i]==1){
x[i]=lower_bound(pos+1,pos+m+1,x[i])-pos;
modify(tree,1,x[i],y[i]);
}
if(op[i]==2){
x[i]=lower_bound(pos+1,pos+m+1,x[i])-pos,y[i]=upper_bound(pos+1,pos+m+1,y[i])-pos-1;
printf("%lld\n",x[i]<=y[i]?query(tree,1,x[i],y[i]).first:0);
}
}
return 0;
}
K 世界泡
阅读理解题。可以对于每一个点记录后手从该点出发的所有路径,直接 dsu on tree 就可以了。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=500010;
const int M=20;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,m,a[N],x,y,head[N],cnt,dis[N],diss[N],val[N],ans;
struct edge{int to,nxt;}e[N<<1];
bool vis[N];
priority_queue<ll>q[N];
void addedge(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
void dfs(int u,int fa){
dis[u]=dis[fa]+a[u],val[u]=val[fa]*(1+vis[fa]),diss[u]=diss[fa]+a[u]*val[u];
ll minn=0x3f3f3f3f;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
minn=min(minn,a[v]);
if(q[u].size()<q[v].size())swap(q[u],q[v]);
while(!q[v].empty())q[u].push(q[v].top()),q[v].pop();
}
while(!q[u].empty()&&(q[u].top()-diss[u])/(val[u]*(1+vis[u]))>=dis[u]-a[1]+minn)q[u].pop();
if(!q[u].empty())ans=max(ans,(q[u].top()-diss[u])/(val[u]*(1+vis[u]))-dis[u]+a[1]);
q[u].push(diss[u]);
}
int main(){
val[0]=1;
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=m;i++)vis[read()]=1;
for(int i=1;i<n;i++){
x=read(),y=read();
addedge(x,y),addedge(y,x);
}
dfs(1,0);
printf("%lld",ans);
return 0;
}
L 树上魔法使
挺难的数学题。
#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=1000010;
const ll mod=998244353;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,m,x,y,head[N],cnt,ans,deg[N],p1[N],p2[N],p3[N],sum;
struct edge{int to,nxt;}e[N<<1];
void addedge(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
ll qp(ll x,ll p){
ll res=1;
while(p){
if(p&1)res=1ll*res*x%mod;
x=1ll*x*x%mod,p>>=1;
}
return res;
}
int main(){
p1[0]=p2[0]=p3[0]=1;
n=read(),m=read();
for(int i=1;i<=1000000;i++)p1[i]=1ll*p1[i-1]*m%mod;
p2[1]=1ll*(m-1)*qp(m,mod-2)%mod,p3[1]=(2ll*p2[1]%mod-1+mod)%mod;
for(int i=2;i<=1000000;i++)p2[i]=1ll*p2[i-1]*p2[1]%mod,p3[i]=1ll*p3[i-1]*p3[1]%mod;
for(int i=1;i<n;i++){
x=read(),y=read(),deg[x]++,deg[y]++;
addedge(x,y),addedge(y,x);
}
ans=1ll*(n-1)*(n-1)%mod*p1[n-1]%mod;
for(int i=1;i<=n;i++)sum=(sum+1-p2[deg[i]]+mod)%mod;
ans=(ans+1ll*p1[n]*m%mod*sum%mod*sum%mod)%mod;
for(int u=1;u<=n;u++){
ans=(ans+2ll*(1-n+mod)*p1[n]%mod*(1-p2[deg[u]]+mod)%mod)%mod;
ans=(ans+mod-1ll*p1[n]*m%mod*(1-p2[deg[u]]+mod)%mod*(1-p2[deg[u]]+mod)%mod)%mod;
ans=(ans+1ll*p1[n]*(1-p2[deg[u]]+mod+m-1-1ll*m*p2[deg[u]]%mod+mod+1ll*(m-2)*(1ll*p2[1]*p3[deg[u]-1]%mod-p2[deg[u]]+mod)%mod)%mod)%mod;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
ans=(ans+mod-1ll*p1[n]*m%mod*(1-p2[deg[u]]+mod)%mod*(1-p2[deg[v]]+mod)%mod)%mod;
ans=(ans+p1[n-1])%mod;
ans=(ans+1ll*p1[n-1]*(m-1)%mod*(1ll*(m-1)*(1-p2[deg[u]-1]+mod)%mod*(1-p2[deg[v]-1]+mod)%mod+1-p2[deg[u]-1]+mod+1-p2[deg[v]-1]+mod)%mod)%mod;
}
}
printf("%lld",ans);
return 0;
}
总结
这场比赛其实出的挺好的,很适合 OI 选手。希望我能知耻而后勇,在后续的省选等比赛中发挥出自己应有的水平,弥补自己一直以来的遗憾!