比赛链接
Solved: 5/9
Upsolved: 6/9
Rank: 542
搞了个很长的大头,之后贴代码都不贴大头了。
#include<bits/stdc++.h>
using namespace std;
using ui=unsigned; using db=long double; using ll=long long; using ull=unsigned long long; using lll=__int128;
using pii=pair<int,int>; using pll=pair<ll,ll>;
template<class T1, class T2> istream &operator>>(istream &cin, pair<T1, T2> &a) { return cin>>a.first>>a.second; }
template<class T1> istream &operator>>(istream &cin, vector<T1> &a) { for (auto &x:a) cin>>x; return cin; }
template<class T1> istream &operator>>(istream &cin, valarray<T1> &a) { for (auto &x:a) cin>>x; return cin; }
template<class T1, class T2> bool cmin(T1 &x, const T2 &y) { if (y<x) { x=y; return 1; } return 0; }
template<class T1, class T2> bool cmax(T1 &x, const T2 &y) { if (x<y) { x=y; return 1; } return 0; }
istream &operator>>(istream &cin, lll &x) { x=0; static string s; cin>>s; for (char c:s) x=x*10+(c-'0'); return cin; }
ostream &operator<<(ostream &cout, lll x) { static char s[60]; int tp=1; s[0]='0'+(x%10); while (x/=10) s[tp++]='0'+(x%10); while (tp--) cout<<s[tp]; return cout; }
#define all(x) (x).begin(),(x).end()
const int mod = 998244353;
const ll inf=4e18;
ll qpow(ll x,ll y=mod-2,int m=mod){ll r=1;for(;y;y>>=1,x=x*x%m)if(y&1)r=r*x%m;return r;}void solve(){}int main(){ios::sync_with_stdio(false);cin.tie(0);int T;cin>>T;//while(T--)cout<<(solve()?"YES":"NO")<<'\n';while(T--)solve();
}
A. MEX Table
题意:将 \(0\) 到 \(nm-1\) 排成一个 \(n\times m\) 矩阵,使行 mex 之和加列 mex 之和最大。
注意到 0 所在行列之外的 mex 均为 0,所以只需让 0 所在行或列 mex 取到 \(n\) 或 \(m\),另一个 mex 取 \(1\)。
void solve(){int n,m;cin>>n>>m;cout<<max(n,m)+1<<'\n';
}
B. Gorilla and the Exam
题意:给一个序列,任意修改其中 \(k\) 个数,使得出现的不同的数最少。
按出现次数从小到大排序,改成出现次数最多的数。
void solve(){int n,k;cin>>n>>k;vector<int> a(n);cin>>a;if(n==k){cout<<"1\n";return;}map<int,int> cnt;for(int i=0;i<n;i++)cnt[a[i]]++;int ans=0;vector<int> b;for(auto x:cnt)b.push_back(x.second);sort(all(b));int m=b.size(),res=m;for(int i=0;i<m;i++){if(k<b[i])break;k-=b[i];--res;}cout<<res<<'\n';
}
C. Trip to the Olympiad
题意:给定区间 \([l,r]\),从中选取三个不同的整数 \(a,b,c\),使 \((a\oplus b) + (b\oplus c) + (c\oplus a)\) 最大。
尽量使每一位都同时出现 0 和 1。直接令 \(a=l,c=r\),从高到低考虑 \(b\) 的二进制位:
-
若 \(l\) 和 \(r\) 的某位相同,且之前未出现过不同,则 \(b\) 也只能与 \(l\) 和 \(r\) 相同;
-
若 \(l\) 和 \(r\) 的某位相同,且之前出现过不同,则 \(b\) 与 \(l\) 和 \(r\) 不同;
-
若 \(l\) 和 \(r\) 的某位不同,且之前未出现过不同,则一定是 \(l\) 的这一位是 \(0\) 而 \(r\) 的这一位是 \(1\)。此时找到这一位之后最高的相同位,若为 \(0\) 则表示 \(b\) 的这一位是 \(1\),倒推回来当前位只能是 \(0\)(否则将大于 \(r\)),为 \(1\) 同理;
-
若 \(l\) 和 \(r\) 的某位不同,且之前出现过不同,则取与第一次不同的位相反的数即可。
void solve(){ll l,r;cin>>l>>r;ll a=l,c=r,b=0;int o=__lg(r);bool fl=1;int t=0;for(int i=o;i>=0;--i){int x=l>>i&1,y=r>>i&1;if(x==y){if(fl)b|=x<<i;else b|=(x^1)<<i;}else{if(fl){for(int j=i;j>=0;--j)if((l>>j&1)==(r>>j&1)){t=l>>j&1;break;}b|=t<<i;fl=0;}else b|=(t^1)<<i;}}cout<<a<<' '<<b<<' '<<c<<'\n';
}
D. Gifts Order
题意:维护一个序列,支持单点修改,全局查询区间极差减区间长度的最大值。
首先最优区间一定左端点和右端点分别为最大和最小值。
若左端点为最大值,则答案是 \(a_l-a_r-r+l=(a_l+l)-(a_r+r)\);若右端点为最大值,则答案是 \(a_r-a_l-r+l=(a_r-r)-(a_l-l)\)。
令 \(b_i=a_i-i,c_i=a_i+i\),则答案是 \(\max\{\max_{l<r}b_r-b_l,\max_{l<r}c_l-c_r\}\)。
对于单点修改,我们可以用线段树维护区间最大值、区间最小值和区间答案。
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)const int N=2e5+5;
int n,q,x,y,a[N],b[N],c[N];
int mxb[N*4],mnb[N*4],mxc[N*4],mnc[N*4],ansb[N*4],ansc[N*4];
void pushup(int x){mxb[x]=max(mxb[lc],mxb[rc]), mnb[x]=min(mnb[lc],mnb[rc]);mxc[x]=max(mxc[lc],mxc[rc]), mnc[x]=min(mnc[lc],mnc[rc]);ansb[x]=max(max(ansb[lc],ansb[rc]),mxb[rc]-mnb[lc]);ansc[x]=max(max(ansc[lc],ansc[rc]),mxc[lc]-mnc[rc]);
}
void build(int x,int l,int r){if(l==r){mxb[x]=mnb[x]=b[l];mxc[x]=mnc[x]=c[l];ansb[x]=ansc[x]=0;return;}build(lc,l,mid);build(rc,mid+1,r);pushup(x);
}
void update(int x,int l,int r,int pos,int vb,int vc){if(l==r){mxb[x]=mnb[x]=vb;mxc[x]=mnc[x]=vc;ansb[x]=ansc[x]=0;return;}if(pos<=mid)update(lc,l,mid,pos,vb,vc);else update(rc,mid+1,r,pos,vb,vc);pushup(x);
}void solve(){cin>>n>>q;for(int i=1;i<=n;++i)cin>>a[i],b[i]=a[i]-i,c[i]=a[i]+i;build(1,1,n);cout<<max(ansb[1],ansc[1])<<'\n';while(q--){cin>>x>>y;b[x]=y-x,c[x]=y+x;update(1,1,n,x,b[x],c[x]);cout<<max(ansb[1],ansc[1])<<'\n';}
}
E. Another Exercise on Graphs
题意:给一张无向图,多次询问 \(x\) 到 \(y\) 的所有路径中第 \(k\) 大边的最小值。\(n\leq 400\)。
最小化第 \(k\) 大一类问题我们有经典的二分做法,将所有小于等于 \(x\) 的边视为 \(0\),大于 \(x\) 的边视为 \(1\),然后求最短路。
但直接套用到本题复杂度是 \(O(qm\log w)\) 甚至连 E1 都过不去。
答案仅可能是某条边的边权,我们考虑从小到大动态加边并维护“将大于当前边权视为 \(1\) 小于等于当前边权视为 \(0\)”的 dis 数组。
每加入一条边相等于将一条 \(1\) 边变为 \(0\) 边,这相当于对所有点对进行一次松弛,需要 \(O(n^2)\) 更新。
本题 \(m=O(n^2)\) 所以直接做是 \(O(n^4)\) 仍然无法通过。但注意到有效松弛(会改变 dis 的松弛)只有 \(n-1\) 次,因此同时维护一个并查集,只有加入不同集合的边时才进入松弛,复杂度就降到了 \(O(n^3)\)。
const int N=405,M=3e5+5;
int n,m,q,x,y,z,dis[N][N],ans[N][N][N];
struct node{int x,y,z;}buc[M],que[M];int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}void solve(){cin>>n>>m>>q;for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(i!=j){dis[i][j]=inf;for(int k=1;k<=n;++k)ans[i][j][k]=inf;}for(int i=1;i<=m;i++){cin>>x>>y>>z;dis[x][y]=dis[y][x]=1;buc[i]={x,y,z};}for(int k=1;k<=n;++k)for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)cmin(dis[i][j],dis[i][k]+dis[k][j]);sort(buc+1,buc+m+1,[](node a,node b){return a.z<b.z;});for(int i=1;i<=q;++i)cin>>x>>y>>z,que[i]={x,y,z};for(int i=1;i<=n;++i)fa[i]=i;for(int i=1;i<=m;++i){int x=buc[i].x,y=buc[i].y;int xx=find(x),yy=find(y);if(xx!=yy){for(int j=1;j<=n;++j)for(int k=1;k<=n;++k){cmin(dis[j][k],min(dis[j][y]+dis[x][k],dis[j][x]+dis[y][k]));if(dis[j][k]<=n&&ans[j][k][dis[j][k]+1]>=inf)ans[j][k][dis[j][k]+1]=buc[i].z;}fa[xx]=yy;}}for(int i=1;i<=q;++i)cout<<ans[que[i].x][que[i].y][min(n,que[i].z)]<<' ';cout<<'\n';
}