Atcoder beginner contest 395(A,B,C,补DEF)
atcoder beginner contest 395
赛时ABC,赛后DEF(希望cf灰名有朝一日能稳定4-5题)
A :直接模拟
B:同上
C:一个map存出现次数,再用一个数组存一个数上一次出现的次数,ans = min(ans, i - lastnumber[a[i]]+1);
```#include<bits/stdc++.h>
#define N 1000005
#define mod 998244353
using namespace std;
typedef long long ll;
int b[N];
void solve()
{
int n;
cin>>n;
vector<int>a(n+1);
map<int,int>mp;
int ans = INT_MAX;
for(int i = 1;i <= n;i++)
{
cin>>a[i];
mp[a[i]]++;
if(mp[a[i]] >=2)
{
//ans = INT_MAX;
ans = min(i-b[a[i]]+1,ans);
}
b[a[i]] = i;
}
if(ans !=INT_MAX) cout<<ans;
else cout<<-1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
while(T--)
solve();
return 0;
}
D:多个数组维护:
n个鸽子和鸟巢,初始鸽子i在鸟巢i
1操作把鸽子a放进鸟巢b
2操作把鸟巢a和鸟巢b鸽子交换
3操作查询鸽子a所在鸟巢编号
idea:乍一看似乎是一个冰茶机问题,但仅仅并查集处理不太行,需要维护一点信息,因为操作A如果是对子树根节点进行处理,
则它的叶子结点会被破坏image
如图,如果要把黄色箭头指向的鸽子放到另一个笼子里,若是简单的并查集吗,则它的叶子节点会被破坏
因此可以这样考虑:
1.一个数组f维护鸽子i在哪个笼子
2.一个数组p维护笼子i的编号
3.一个数组r维护编号为i的笼子的位置、
则对于操作1:f[a] = r[b]
对于操作2:p[f[a]] = b,p[f[b]] = a,swap(r[a],r[b]);**
因为这里要把a中所有鸽子放到b笼子里,相当于把a所在的笼子的门牌号换为b,b同理,同时注意修改r数组
对于操作3:则只需输出p[f[a]]即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int f[N],p[N],r[N];
void init()
{
for(int i = 1;i <= N-1;i++)
{
f[i] = i;//鸽子编号
p[i] = i;//鸽子所在笼子的门牌号
r[i] = i;//门牌号为i对应的鸽子笼编号
}
}
void solve()
{
int n,q;
cin>>n>>q;
init();
while(q--)
{
int op;
cin>>op;
if(op==1)
{
int a,b;
cin>>a>>b;
f[a] = r[b];
}
if(op==2)
{
int a,b;
cin>>a>>b;
p[r[a]] = b;
p[r[b]] = a;
swap(r[a],r[b]);
}
if(op==3)
{
int x;
cin>>x;
cout<<p[f[x]]<<"\n";
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
E:分层图+最短路
题意描述:给定一个X,对一个N个顶点M条边的有向图,从1->N的最小代价,每条边的边权为1,同时可以进行反转操作,花费X的代价将u->v的边反置为v->u,边权也为1
idea:考虑两层的分层图,对于每个节点自身,边权设为X即可,使用优先队列优化后即为dij模板
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
vector<pair<int,int>>G[N*2];
priority_queue<pair<long long ,int>,vector<pair<long long ,int>>,greater<pair<long long ,int>>>q;
ll dis[N*2];
bool used[N*2];
int n,m,x;
void addedge(int u,int v,int w)
{
G[u].emplace_back(v,w);
}
void solve()
{
cin>>n>>m>>x;
for(int i = 1;i <= m;i++)
{
int u,v;
cin>>u>>v;
addedge(u,v,1);
addedge(v+n,u+n,1);
}
for(int i = 1;i <= n;i++)
{
addedge(i,i+n,x);
addedge(n+i,i,x);
}
for(int i = 1;i <= 2*n;i++) dis[i] = 1e18;
dis[1]=0;
q.push({0,1});
while(!q.empty())
{
auto [u,v] = q.top();
q.pop();
if(used[v]) continue;
used[v] = 1;
for(auto [to,w]: G[v])
{
if(dis[to] > dis[v] + w)
{
dis[to] = dis[v]+w;
q.push({dis[to],to});
}
}
}
cout<<min(dis[n],dis[2*n]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
F:二分答案(差分约束)
题意描述:长度为n的序列分别为u[i]与d[i],给定一个X;对于每个正数,可以花费1的代价使其减小1,能否找到最小花费满足下列条件
1:u[i] + d[i] = H;(固定的数)
2.对于每个1 < i <= n,|u[i]-u[i-1]| <=X
idea:思考,只要确定一个H,即可算出最后的答案,同时答案可以转化为\sum_{i=1}^{n} (u[i]+d[i]) - H
要使得花费最小,则H应尽可能大,那么这里考虑H的单调性,对于H,如果H成立,则H-1也一定成立
下面是证明:
对于正数序列,如果修改后的序列为
u'[1],u'[2]..u;[n]
d'[1],d'[2],d'[n]
如果把H修改为H-1,则考虑把每个u'[i]变为u[i]-1,那么对于相邻的数,他们的差不变,既然H满足\abs{u[i]-u[i-1]} <= X,则显然H-1也满足#证毕
则现在考虑使用二分答案,来找到一个最大的H,同时使得序列满足条件
那么现在考虑两个条件,对于1,2,显然1更容易满足
现在假设修改后的u'[i] = t,则d'[i] = H-t;
对于2,假设修改后满足条件的数为u'[i],那么显然有u'[i] ≤ u[i],d'[i] ≤ d[i];
则有t ≤ u[i],H-t ≤ d[i] ---> max(0, H-d[i] )≤ t ≤ u[i]
那么对于修改后的每个u'[i],都有一个对应的区间使得 L[i] ≤ t ≤ R[i]
其中的L[i] 应该同时满足两个条件,因此,L[i] = max(H-d[i] , lastL - X )
lastL表示上一个u'[i]的取值,要满足abs(u'[i-1] - t) ≤ X ,则 u'[i-1] - X ≤ t ≤ u'[i-1] + X
对于R[i]也应该同时满足两个条件,因此,R[i] = min(u[i],lastR + X)
#include<bits/stdc++.h>
#define N 1005
#define mod 998244353
using namespace std;
typedef long long ll;
void solve()
{
int n,x;
cin>>n>>x;
ll l = 0,r = 2e9;
ll sum = 0;
vector<int>u(n+1),d(n+1);
for(int i = 1;i <= n;i++)
{
cin>>u[i]>>d[i];
sum+=u[i] + d[i];
r = min<ll>(r,u[i]+d[i]);
}
ll ans = -1;
auto check = [&](ll H)
{
ll lstL = max(0ll,H-d[1]),lstR = u[1];
for(int i =2 ;i <= n;i++)
{
lstL = max<ll>(max<ll>(lstL-x,H-d[i]),0);
lstR = min<ll>(lstR + x,u[i]);
if(lstL > lstR) return false;
}
return true;
};
while(l <= r)
{
ll mid = (l+r)>>1;
if(check(mid)) ans = mid,l = mid+1;
else r = mid - 1;
}
cout<<sum - (ans*n)<<"\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
while(T--)
solve();
return 0;
}