2022.11.03 NOIP2022 模拟赛二
绯色 IOI(开端)
之前做过了,见杂题题解(一),话说这个系列是不是好久没更新了。
Code
const int N=2e5+5;
int n,m,a[N];
int main() {
n=read();
FOR(i,1,n) a[i]=read();
sort(a+1,a+n+1);
ll ans=0;
for(int j=1;j<n-1;j++) ans+=1ll*(a[j]-a[j+2])*(a[j]-a[j+2]);
ans+=1ll*(a[1]-a[2])*(a[1]-a[2])+1ll*(a[n]-a[n-1])*(a[n]-a[n-1]);
printf("%lld\n",ans);
}
绯色 IOI(抵达)
首先先用一个类似 bfs 的过程求出每一个点的庇护所是哪一个点,具体使用 set 稍微维护一下就行。
根据庇护所的关系,可以得到若干个危险指数间的大小关系,将这些关系建边,用堆拓扑排序即可。
Code
const int N=5e5+5;
vi G[N],D[N];
set<int> f[N];
int p[N],don[N],deg[N],a[N],cnt;
int main() {
int n;scanf("%d",&n);
if(n%2==1) return puts("-1"),0;
FOR(i,1,n-1) {
int x,y;scanf("%d %d",&x,&y);
G[x].pb(y),G[y].pb(x);
}
FOR(u,1,n) for(int v:G[u]) f[u].insert(v);
queue<int> q1;
FOR(u,1,n) if(sz(f[u])==1) don[u]=1,q1.push(u);
while(sz(q1)) {
int u=q1.front();q1.pop();
p[u]=*f[u].begin();
for(int v:G[p[u]]) {
f[v].erase(p[u]);
if(don[v]&&!p[v]) return puts("-1"),0;
}
for(int v:G[p[u]]) if(sz(f[v])==1) don[v]=1,q1.push(v);
}
FOR(u,1,n) for(int v:G[u]) if(v!=p[u]) D[p[u]].pb(v),deg[v]++;
priority_queue<int,vector<int>,greater<int> > dq;
FOR(i,1,n) if(deg[i]==0) dq.push(i);
while(sz(dq)) {
int u=dq.top();dq.pop();
a[++cnt]=u;
for(int v:D[u]) if(--deg[v]==0) dq.push(v);
}
if(cnt!=n) return puts("-1"),0;
FOR(i,1,n) printf("%d ",a[i]);
puts("");
}
绯色 IOI(危机)
因为转移代价没有性质,大胆猜测暴力建出 DAG 复杂度是对的。
事实上确实是这样,我们求出每个点可以间接或直接炸到哪里,建出 DAG 拓扑排序 DP 即可。
代码中使用了单调栈来求出区间。
Code
const int N=3e5+5,mod=998244353;
struct bomb {ll x,r;int v,id;} b[N];
bool cmp(bomb a,bomb b) {return a.x<b.x;}
int val[N],deg[N],rid[N],l[N],r[N],st[N],top;
ll f[N],x[N],R[N];
vi G[N];
int main() {
int n;scanf("%d",&n);
FOR(i,1,n) scanf("%lld",&b[i].x),b[i].id=i;
FOR(i,1,n) scanf("%lld",&b[i].r);
FOR(i,1,n) scanf("%d",&b[i].v);
sort(b+1,b+n+1,cmp);
FOR(i,1,n) x[i]=b[i].x,R[i]=b[i].r,val[i]=b[i].v,rid[b[i].id]=i;
FOR(i,1,n) {
l[i]=lower_bound(x+1,x+n+1,x[i]-R[i])-x;
r[i]=upper_bound(x+1,x+n+1,x[i]+R[i])-x-1;
}
FOR(i,1,n) {
while(top&&st[top]>=l[i]) chkmin(l[i],l[st[top]]),top--;
st[++top]=i;
}
top=0;
ROF(i,n,1) {
while(top&&st[top]<=r[i]) chkmax(r[i],r[st[top]]),top--;
st[++top]=i;
}
FOR(i,1,n) FOR(j,l[i],r[i]) if(j!=i) G[i].pb(j),deg[j]++;
queue<int> q;
FOR(i,1,n) if(deg[i]==0) q.push(i);
while(sz(q)) {
int u=q.front();q.pop();
for(int v:G[u]) {
f[v]=max(f[v],f[u]+(1ll*val[u]*val[v]%mod+(val[u]^val[v])%mod)%mod);
if(--deg[v]==0) q.push(v);
}
}
FOR(i,1,n) printf("%lld\n",f[rid[i]]);
}
绯色 IOI(悬念)
我们将问题稍微转化一波,考虑对于两条从左部点 \(a_i\) 出发的边,设他们连向 \(b_i,b_j\),改为将 \(b_i,b_j\) 连一条无向边,要求将所有的无向边定向,使得每个点入度为 \(1\) 并且对应的权最大。
因为左侧满匹配,利用 Hall 定理得到,在左侧的任意一个 \(k\) 的点集,往右边连到的点集大于等于左边点的个数,左边是边而右边是点,故图的任意一个连通块为基环树或树。
基环树是简单的,因为每个点入度为 \(1\),故树边一定是叶向的,而环边要么是顺时针要么是逆时针的,这些都很好维护。
树的话考虑定根,定成叶向树,维护每个点的答案,若一条边定成 \(u\to v\),则根不在 \(v\) 的子树内会有贡献,否则则根在 \(v\) 的子树内会有贡献。
区间加维护区间 \(\max\) 即可。
Code
const int N=5e5+5;
int n,m,T;
int a[N],b[N],dfn[N],re[N],dfc,rt[N],pre[N],dep[N];
int head[N],son[N*2],nxt[N*2],tot,rev[N*2],val[N*2],typ[N*2],sum[N][3],fl[N];
vi edg;
int ans,cur;
void add(int x,int y) {son[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
void dfs(int u) {
for(int i=head[u],v;i;i=nxt[i]) {
if(!dep[v=son[i]]) pre[v]=i,dep[v]=dep[u]+1,dfs(v);
else if(dep[v]>=dep[u]) {
int j=pre[v];
while(j&&son[j]!=u) edg.pb(j),j=pre[son[rev[j]]];
edg.pb(rev[i]);
}
}
}
void dfs2(int u) {
dfn[u]=++dfc,rt[u]=cur;
for(int i=head[u],v;i;i=nxt[i]) if(!dfn[v=son[i]]) dfs2(v);
re[u]=dfc;
}
int ma[N*4],laz[N*4];
void pushadd(int p,int v) {ma[p]+=v,laz[p]+=v;}
void pushdown(int p) {
if(laz[p]) {
pushadd(p*2,laz[p]),pushadd(p*2+1,laz[p]);
laz[p]=0;
}
}
void pushup(int p) {ma[p]=max(ma[p*2],ma[p*2+1]);}
void change(int p,int l,int r,int x,int y,int v) {
if(x>y||y<l||x>r) return;
if(x<=l&&r<=y) return pushadd(p,v);
int mid=(l+r)>>1;
pushdown(p);
if(x<=mid) change(p*2,l,mid,x,y,v);
if(y>mid) change(p*2+1,mid+1,r,x,y,v);
pushup(p);
}
int query(int p,int l,int r,int x,int y) {
if(x>y||y<l||x>r) return 0;
if(x<=l&&r<=y) return ma[p];
int mid=(l+r)>>1,m=0;
pushdown(p);
if(x<=mid) chkmax(m,query(p*2,l,mid,x,y));
if(y>mid) chkmax(m,query(p*2+1,mid+1,r,x,y));
return m;
}
void change(int x,int v) {
int dt=v-val[x],R=rt[son[x]];
val[x]=v;
if(fl[R]) {
ans-=sum[R][0]+max(sum[R][1],sum[R][2]);
if(typ[x]||dfn[son[x]]>dfn[son[rev[x]]])
sum[R][typ[x]]+=dt;
ans+=sum[R][0]+max(sum[R][1],sum[R][2]);
}
else {
ans-=query(1,1,n,dfn[R],re[R]);
if(dfn[son[x]]>dfn[son[rev[x]]])
change(1,1,n,dfn[R],dfn[son[x]]-1,dt),
change(1,1,n,re[son[x]]+1,re[R],dt);
else
change(1,1,n,dfn[son[rev[x]]],re[son[rev[x]]],dt);
ans+=query(1,1,n,dfn[R],re[R]);
}
}
int main() {
m=read(),n=read(),T=read();
FOR(i,1,m) a[i]=read();
FOR(i,1,m) b[i]=read();
FOR(i,1,m) {
int x=(a[i]-b[i]+n)%n,y=(a[i]+b[i])%n;
if(x>y) swap(x,y);
x++,y++,add(y,x);
if(x!=y) rev[tot]=tot+1,add(x,y),rev[tot]=tot-1;
else rev[tot]=tot;
}
FOR(i,1,n) if(!rt[i]) {
edg.clear(),cur=i,dep[i]=1;
dfs(i),fl[i]=(sz(edg)>0);
if(fl[i]) {
for(int j:edg) typ[j]=1,typ[rev[j]]=2;
dfs2(son[edg[0]]);
}
else dfs2(i);
}
FOR(i,1,tot) change(i,read());
printf("%d\n",ans);
int q=read();
FOR(i,1,q) {
int x=read()-T*ans,y=read()-T*ans;
change(x,y),printf("%d\n",ans);
}
}