CF Round 1002 题解合集
困难场。
C
重要观察:只有每行的后缀 有用。
考虑归纳证明,设 为一行后缀 的个数。
对于 的情况,想要让 值为 ,必须选择这个后缀,否则其他后缀的和 一定 ;
对于 的情况,在之前,每次选择后缀 都是在再不选就不行的情况下,比如在最后一个时刻选 ,在倒数第二个时刻选 之类,所以这个时刻也没有选择,只能选后缀 。
因此,我们保留每一行的极长后缀 个数,贪心减小若干值最大化 即可。
具体贪心的过程为如果当前数不存在,那么选择最接近它的大于它的数来减小。
总体复杂度 。
#include<bits/stdc++.h>
using namespace std;
int t,n,a[301][301],sum[301],flag,cnt,ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
cnt=0;
for(int j=n;j>=1;j--){
if(a[i][j]!=1) break;
cnt++;
}
sum[cnt]++;
}
for(int i=0;i<=n;i++){
if(sum[i]) continue;
flag=false;
for(int j=i+1;j<=n;j++){
if(sum[j]){
sum[j]--;
flag=true;
break;
}
}
if(!flag){
ans=i;
break;
}
}
cout<<ans<<'\n';
for(int i=0;i<=n;i++) sum[i]=0;
ans=0;
}
return 0;
}
D
考虑想要使得代价为定值,那么一定是走到了图上相同的两点 ,满足 有相同的出点 。
因为点数很小,考虑新建一张图,每个点 ,表示在原图第一张图上走到点 ,原图第二张图上走到点 ,然后连边就是枚举 的出点直接连。
这样总点数不会超过 ,总边数不会超过 ,合法的目标点就是上文所说的合法的 。
总体复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int t,n,s1,s2,x,y,dcnt,m1,m2,ans,id[1001][1001];
bool vis[1001][1001],chk[1001];
vector<int> v[1001],g[1001];
int head[1000001],nxt[4000001],targetx[4000001],targetw[4000001],tot;
void add(int x,int y,int w){
tot++;
nxt[tot]=head[x];
head[x]=tot;
targetx[tot]=y;
targetw[tot]=w;
}
struct node{
int x,dis;
bool operator <(const node &a)const{
return a.dis<dis;
}
};
priority_queue<node> q;
int dis[1000001],ok[1000001];
void dij(int s){
for(int i=1;i<=dcnt;i++){
dis[i]=inf;
ok[i]=0;
}
dis[s]=0;
q.push((node){s,dis[s]});
while(q.size()){
int x=q.top().x;
q.pop();
if(ok[x]) continue;
ok[x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=targetx[i],w=targetw[i];
if(dis[y]>dis[x]+w){
dis[y]=dis[x]+w;
q.push((node){y,dis[y]});
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n>>s1>>s2;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
id[i][j]=++dcnt;
}
}
cin>>m1;
for(int i=1;i<=m1;i++){
cin>>x>>y;
vis[x][y]=1;
vis[y][x]=1;
v[x].push_back(y);
v[y].push_back(x);
}
cin>>m2;
for(int i=1;i<=m2;i++){
cin>>x>>y;
if(vis[x][y]) chk[x]=chk[y]=1;
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int l1=0;l1<v[i].size();l1++){
for(int l2=0;l2<g[j].size();l2++){
x=v[i][l1],y=g[j][l2];
add(id[i][j],id[x][y],abs(x-y));
}
}
}
}
dij(id[s1][s2]);
ans=inf;
for(int i=1;i<=n;i++){
if(chk[i]) ans=min(ans,dis[id[i][i]]);
}
if(ans<inf) cout<<ans<<'\n';
else cout<<-1<<'\n';
for(int i=1;i<=n;i++){
v[i].clear();
g[i].clear();
chk[i]=0;
}
for(int i=1;i<=dcnt;i++){
head[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
vis[i][j]=0;
}
}
tot=dcnt=0;
}
return 0;
}
E1
困难题。
考虑将矩形摊成序列上,那么相当于我们可以在 个位置加数,并且在 个位置删数。
考虑最小化操作次数,一定是最大化不动的点的个数,并且这些不动的点,构成了 的前缀与 的子序列。
从小到大枚举 ,设 为 在 中的匹配位置,考虑前缀 是否合法。
-
如果 中不存在 ,死;
-
如果 死;
-
如果 并且 不是块的右端点,死。
如果 死了,那么有可能整个块都死了。
所以考虑如果块的左端点到匹配位置的距离比 到右端点的距离更小,那么整个块都死了。
否则只有 这个点会死。
做完了,总体复杂度 。
#include<bits/stdc++.h>
using namespace std;
int t,n,m,a[300005],b[300005],pos[600005],bel[300005],flag,ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n>>m;
n*=m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
pos[b[i]]=i;
}
for(int i=1;i<=n;i++){
bel[i]=(i-1)/m+1;
}
for(int i=1;i<=n;i++){
if(!pos[a[i]]) flag=true;
if(i>=2 && pos[a[i]]-i<pos[a[i-1]]-(i-1)) flag=true;
if(i>=2 && pos[a[i]]-i>pos[a[i-1]]-(i-1) && pos[a[i-1]]<m*bel[i-1]) flag=true;
if(flag){
ans=true;
int p=(bel[i]-1)*m+1;
if(p+m-i-1>=pos[a[p]]-p) cout<<n-p+1<<'\n';
else cout<<n-i+1<<'\n';
break;
}
}
if(!ans) cout<<0<<'\n';
ans=flag=0;
for(int i=1;i<=n;i++){
pos[b[i]]=0;
}
}
return 0;
}
本文作者:Kenma
本文链接:https://www.cnblogs.com/Kenma/p/18714301
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步