AtCoder Regular Contest 147
\(\texttt{Rating Change:}\color{green}{1049}\color{black}\to \color{Turquoise}{1356}\)
\(\Delta={\color{green}{\texttt{307}}}\qquad \texttt{rank:251}\)
A
考虑到模不会超过 \(\log\) 次。所以直接用双端队列模拟就可以了。
My Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<' '<<a<<' '
#define pts(a) cerr<<#a<<' '<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=2e5+10;
int a[MAXN];
deque<int> q;
void solve(){
int n;cin>>n;
rep(i,1,n) cin>>a[i];
sort(a+1,a+1+n);
rep(i,1,n) q.push_back(a[i]);
int ans=0;
while(q.size()>1){
int mn=q.front(),mx=q.back();
q.pop_back();
int res=mx%mn;ans++;
if(res) q.push_front(res);
}
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// int T;for(cin>>T;T--;)
solve();
return 0;
}
B
显然是先把奇数放到奇数位上,偶数放到偶数位上,然后奇偶位分开跑冒泡。这样只需要要求交换奇偶位的次数不超过 \(2000\) 次。那随便构造一个方案好像都是可以的吧。
讲个笑话,赛时冒泡排序写挂了。
My Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define pb emplace_back
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<' '<<a<<' '
#define pts(a) cerr<<#a<<' '<<a<<'\n'
//#define int long long
using namespace std;
const int MAXN=410;
struct Ans{
char op;int p;
};
vector<Ans> ans;
int a[MAXN];
void Bmov(int i,int j){
if(i<j){
while(i<j) ans.pb(Ans{'B',i}),swap(a[i],a[i+2]),i+=2;
}else{
while(i>j) ans.pb(Ans{'B',i-2}),swap(a[i],a[i-2]),i-=2;
}
}
void Bswap(int i){
ans.pb(Ans{'B',i});
swap(a[i],a[i+2]);
}
void Aswap(int i){
ans.pb(Ans{'A',i});
swap(a[i],a[i+1]);
}
void solve(){
int n;cin>>n;
rep(i,1,n) cin>>a[i];
vector<int> eve,odd;
for(int i=2;i<=n;i+=2)
if(a[i]&1) eve.pb(i);
for(int i=1;i<=n;i+=2)
if(a[i]%2==0) odd.pb(i);
per(i,siz(odd)-1,0){
auto p=lower_bound(all(eve),odd[i]);
if(p==eve.end()) p--,Bmov(*p,odd[i]-1),Aswap(odd[i]-1);
else Bmov(*p,odd[i]+1),Aswap(odd[i]);
eve.erase(p);
}
for(int i=2;i<=n;i+=2)
for(int j=n-(n&1);j>i;j-=2)
if(a[j-2]>a[j]) Bswap(j-2);
for(int i=1;i<=n;i+=2)
for(int j=n-(n%2==0);j>i;j-=2)
if(a[j-2]>a[j]) Bswap(j-2);
cout<<siz(ans)<<'\n';
for(auto as:ans) cout<<as.op<<' '<<as.p<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// int T;for(cin>>T;T--;)
solve();
return 0;
}
C
大概想法就是一堆人挤在一起看起来很优的样子。所以猜想 \(10^7\) 大概是想让我枚举每个位置。于是想到枚举一个中心位置,然后所有人都往这个点靠。这个东西预处理前缀和,以及前缀的答案,可以优化到单次 \(O(1)\)。
接下来简单考虑一下这样做的正确性。想要比上面最优的情况还优,那就只能在上面的基础上优化两边区间不包含中心位置的内部的答案了。要想优化只能把某些人移远中心点。不难发现这样肯定是更劣的。
My Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<' '<<a<<' '
#define pts(a) cerr<<#a<<' '<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=3e5+10;
struct rg{
int l,r;
void input(){cin>>l>>r;}
}r1[MAXN],r2[MAXN];
int pre[MAXN],suf[MAXN],pre_ans[MAXN],suf_ans[MAXN];
void solve(){
int n;cin>>n;
rep(i,1,n) r1[i].input(),r2[i]=r1[i];
sort(r1+1,r1+1+n,[&](rg a,rg b){return a.r<b.r;});
sort(r2+1,r2+1+n,[&](rg a,rg b){return a.l<b.l;});
rep(i,1,n) pre[i]=pre[i-1]+r1[i].r,pre_ans[i]=pre_ans[i-1]+r1[i].r*(i-1)-pre[i-1];
per(i,n,1) suf[i]=suf[i+1]+r2[i].l,suf_ans[i]=suf_ans[i+1]+suf[i+1]-r2[i].l*(n-i);
int t1=0,t2=0,ans=INF;
rep(i,1,10000000){
while(t1<n&&r1[t1+1].r<i) t1++;
while(t2<=n&&r2[t2].l<=i) t2++;
int L=(i*t1-pre[t1])*(n-t1);
int R=(suf[t2]-i*(n-t2+1))*(t2-1);
ans=min(ans,pre_ans[t1]+suf_ans[t2]+L+R);
}cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// int T;for(cin>>T;T--;)
solve();
return 0;
}
D
结论是 \(ans=n^m\times m^{n-1}\)。赛时没有证。
证明:
令 \(X_i\) 为集合 \(S_i\) 和 \(S_{i+1}\) 中不同的那个元素。那么如果说序列 \(X_1,X_2,\cdots,X_{n-1}\) 是确定的,那么当 \(S_1\) 确定了之后,整一个集合序列也就确定了。令 \(A_x\) 表示 \(S_1\) 中包含 \(x\) 的时候,有多少个集合是包含 \(x\) 的,同理 \(B_x\) 表示不包含 \(x\) 的时候,有多少个集合是包含 \(x\) 的。那么当前的 \(X\) 序列无论长什么样,都有 \(A_x+B_x=n\) 成立。并且此时我们的答案应该就是 \(\prod_{i=1}^m(A_i+B_i)=n^m\)。
然后考虑 \(X\) 序列是可以随便构造的,所以不同的方案数总共有 \(m^{n-1}\),每一种的答案都是 \(n^m\),所以最后的结果就是 \(n^m\times m^{n-1}\)。
E
赛时没时间好好做了,只能直接看题解。
首先直接考虑判断 -1
,很显然就是对 \(A,B\) 分别排序之后看是否有 \(A_i\ge B_i\)。
这玩意儿有一个等价的表示,即对于 \(\forall t\in [1,10^9]\),都有小于等于 \(t\) 的 \(B_i\) 的数量减去小于等于 \(t\) 的 \(A_i\) 的数量是大于等于 \(0\) 的。
考虑怎么样使得交换了分数的集合最小。首先起初不合法的肯定是需要交换的,考虑应该把哪些人加进来使得加入最少的人能够满足要求。很显然,对于在不合法的集合内的人,我们可以把他们两个指标分离开来然后重新分配。然后对于一个人,如果重新分配后还是不合法,说明需要一个外面合法的人来共同分担一下,即需要找到一个人 \(j\) 使得 \(B_j\le A_i\) 并且 \(A_j\ge B_i\)。但是我们不可能对于每个人都双关键字去找这个替代的人,因为有可能可以加入一个 \(A_j<B_i\),然后再加入一对 \(A_k,B_k\) 也能满足。所以应当在 \(B_j\le A_i\) 的情况下,选取 \(A_j\) 最大的。
具体地就是,把不合法的都存到一个 \(map\) 里,在 \(A\) 的位置减一,在 \(B\) 的位置加一,然后遍历这个 \(map\)。每次把符合 \(B_j\le A_i\) 的 \(A_j\) 加入到一个堆中,后面就直接取就行了。然后每个元素都只会入队一次,所以复杂度是 \(O(n\log n)\)。
My Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<' '<<a<<' '
#define pts(a) cerr<<#a<<' '<<a<<'\n'
//#define int long long
using namespace std;
const int MAXN=3e5+10;
struct Stu{
int A,B;
bool friend operator<(Stu a,Stu b){
if(a.B==b.B) return a.A>b.A;
else return a.B>b.B;
}
};
priority_queue<Stu> q1;
priority_queue<int> q2;
map<int,int> cnt;
void solve(){
int n;cin>>n;
int nd=0;
rep(i,1,n){
int a,b;cin>>a>>b;
if(a>=b) q1.push(Stu{a,b});
else cnt[b]++,cnt[a]--,nd++;
}
int sum=0;
for(auto cur:cnt){
while(!q1.empty()&&q1.top().B<=cur.fi)
q2.push(q1.top().A),q1.pop();
sum+=cur.se;
while(sum<0){
if(q2.empty()||q2.top()<=cur.fi){
cout<<"-1\n";return;
}cnt[q2.top()]--;nd++;q2.pop();sum++;
}
}cout<<n-nd;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// int T;for(cin>>T;T--;)
solve();
return 0;
}