23ICPC合肥站补题
题解参考1
题解参考2
原题
E 思维
曼哈顿距离 行列可以分开求贡献
对每个点,贡献就是和前面的每个点行列数做差,每个点会有O(n2)O(n^2)O(n2)复杂度,通过计数优化到O(1)O(1)O(1)
最后复杂度O(n2)O(n^2)O(n2),用map会另外有logn2logn^2logn2的复杂度导致超时
unordered_map<int,vector<int>>cx,cy;//用map超时 void solve(){int n,m;cin>>n>>m;forr(i,1,n){forr(j,1,m){int c;cin>>c;cx[c].push_back(i);cy[c].push_back(j);}}//行列分别求int ans=0;for(auto [c,x]:cx){int sm=0,cnt=0;sort(x.begin(),x.end());//和前面每个数做差for(auto i:x){ans+=(i*cnt-sm),cnt++,sm+=i;}}for(auto [c,y]:cy){int sm=0,cnt=0;sort(y.begin(),y.end());//和前面每个数做差for(auto i:y){ans+=(i*cnt-sm),cnt++,sm+=i;}}cout<<ans*2<<endl;
}
F 模拟
void solve(){int n;cin>>n;forr(i,1,n){string s;cin>>s;c[s]++;}for(auto [s,num]:c){if(num*2>n)return cout<<s<<endl,void();}cout<<"uh-oh"<<endl;
}
J 最短路 思维
关键问题是怎么找路上的最大两个值
首先找到最大值后,次大值一定在最大值两侧
把每条边假设为最大值,维护两侧的可能次大值,求最小答案
vector<pii>e[N];
int n,m;auto dij(int st){vector<int>dis(n+1,inf),vis(n+1,0);dis[st]=0;priority_queue<pii>q;//优先队列优化 默认大根堆 用-dis是为了找最小的dis 贪心地让路径代价最小q.push({0,st});//注意pair<int,int>先对first排序while(q.size()){auto [d,v]=q.top();q.pop();if(vis[v])continue;vis[v]=1;for(auto [x,w]:e[v]){if(vis[x])continue;//节省空间时间if(max(w,-d)<dis[x]){dis[x]=max(w,-d);//更新来路上 走到现在的最大值q.push({-dis[x],x});}}}return dis;
}
void solve(){cin>>n>>m;vector<int>ua,va,wa;ua=va=wa=vector<int>(m+1);forr(i,1,m){int u,v,w;cin>>u>>v>>w;e[u].push_back({v,w});//无向图e[v].push_back({u,w});ua[i]=u,va[i]=v,wa[i]=w;}vector<int>d1=dij(1),dn=dij(n);//d1从1走来的最大值 dn从n走来的最大值int ans=inf*2;//枚举每条路径 找两边次大值forr(i,1,m){//u,v都可能离1更近,正反都更新一遍int tp=max(d1[ua[i]],dn[va[i]]);if(tp<=wa[i])ans=min(ans,tp+wa[i]);tp=max(dn[ua[i]],d1[va[i]]);if(tp<=wa[i])ans=min(ans,tp+wa[i]);}cout<<ans<<endl;
}
G 二分 dp
用二分枚举第kkk大的1串的最大长度
dp来check修改次数是否符合
const int N=2e5+2,V=2e4+5,mod=1e9+7,inf=1e9+10;
int n,m,k;
string s;
//每段之间以0分割
int dp[N][7][2];//dp[位置][前面已经有多少段长度>=x][结尾0/1]
int pre[N];
bool check(int x){ memset(dp,0x3f,sizeof dp);int res=inf;//最小修改次数dp[0][0][0]=dp[0][0][1]=0;forr(i,1,n){if(s[i]=='0')dp[i][0][0]=0,dp[i][0][1]=1;else dp[i][0][1]=0;forr(j,1,k){//前面有j个>=x长度的段 //没有新的段dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]);//从上一个位置转移过来 取最小值//有新的段 从上一段转移 结尾是1if(i-x>=0&&j-1>=0)//分段之间必须是0//i-x~x的部分都改成1 代价:pre[i]-pre[i-x]if(s[i-x]!='1')dp[i][j][1]=dp[i-x][j-1][0]+pre[i]-pre[i-x];}}forr(i,1,n)res=min({res,dp[i][k][1],dp[i][k][0]});return res<=m;
}
void solve(){cin>>n>>m>>k;cin>>s;s=' '+s;vector<int>len;int v=0;//统计已经有的1串 (加不加都能过 怀疑这里有hack)/*4 4 21001*/forr(i,1,n){v+=s[i]=='1';if(s[i]=='0'||i==n){if(v>0){len.push_back(v);}v=0;}}sort(len.begin(),len.end(),greater<int>());forr(i,1,n){pre[i]=pre[i-1]+(s[i]=='0');}//二分int l=1,r=n,ans=-1;while (l<r){// int mid=(l+r+1)>>1;int mid=(l+r+1)>>1;if(check(mid))l=mid,ans=mid;else r=mid-1;}if(len.size()>=k)ans=max(len[k-1],ans);cout<<ans<<endl;
}