Educational Codeforces Round 117 (Rated for Div. 2)
Educational Codeforces Round 117 (Rated for Div. 2)
A. Distance
https://codeforces.com/contest/1612/problem/A
题目给出的条件是
距离为曼哈顿距离,而曼哈顿距离等价于步长。
由题目的一半条件,可以得到步长和为AB步长,各自步长为AB步长的一半。
所以显然可以推出:
//原始代码
#include<bits/stdc++.h>
using namespace std;
int t;
int main()
{
scanf("%d",&t);
while(t--)
{
int x,y;
scanf("%d%d",&x,&y);
if((x+y)%2!=0) printf("-1 -1\n");
else if(x%2==0&&y%2==0) printf("%d %d\n",x/2,y/2);
else if(x%2!=0&&y%2!=0)
{
if(x>y) printf("%d %d\n",(x+y>>1)-y,y);
else printf("%d %d\n",x,(x+y>>1)-x);
}
}
return 0;
}
//update
#include<bits/stdc++.h>
using namespace std;
int t;
int main()
{
scanf("%d",&t);
while(t--)
{
int x,y;
scanf("%d%d",&x,&y);
int p=x+y,q=x/2;
if(p%2!=0) printf("-1 -1\n");
else printf("%d %d\n",q,p/2-q);
}
return 0;
}
B. Special Permutation
https://codeforces.com/contest/1612/problem/B
题目为1-n的序列,构造长度为n/2的双序列,保证a在且为左半边最小,b在且为右半边最大
那么显然我们可以这样构造
a放在最前面,然后使得左半边最大。同理右边
#include<bits/stdc++.h>
using namespace std;
int cnt,t,n,a,b;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&a,&b);
int t=n/2;
if(a==t+1&&b==t-1) {for(int i=a;i<=n;i++) printf("%d ",i);for(int i=b;i>=2;i--) printf("%d ",i);printf("1\n");}
else if(a>t||b<t) puts("-1");
else {for(int i=n,cnt=0;cnt==t;i--) if(i!=b) printf("%d%c",i,(++cnt==t?'\n':' '));for(int i=1,cnt=0;cnt==t;i++) if(i!=a) printf("%d%c",i,(++cnt==t:'\ ':' '));}
}
C. Chat Ban
https://codeforces.com/contest/1612/problem/C
题目是一个三角形结构,加一个等差数列,要求第一个超过的列数是多少。
直接二分即可。
这是一个非常经典而又明显的二分题。二分题非常重要的是两个部分,一个是二分性,另外一个是check函数。这里cite一下tutorial。这句话讲的很好。
If we get banned after yy messages, we also get banned after y+1y+1, y+2y+2 and so on messages (and vice versa, if we don't get banned after yy messages, we also don't get banned after y−1y−1, y−2y−2 and so on messages).
三角形的结构,分为两部分,一部分是y<=k,这一部分直接用等差数列求和公式,另外一部分y>k,这一部分需要用一半加上等差的求和
最后得到公式
二分找到第一个>=的即可
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll k,x,all;
int t;
ll get(int x){return x*1ll*(x+1)/2;}
bool check(ll mid)
{
if(mid<=k) return get(mid)>=x;
else return (get(k)+get(k-1)-get(2*k-1-mid))>=x;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%llu%llu",&k,&x);
ll l=0,r=2*k-1;
while(l<r)
{
ll mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%llu\n",r);
}
return 0;
}
D. X-Magic Pair
https://codeforces.com/contest/1612/problem/D
一道简单的数学题
看到操作set =d,很容易发现 这是一个单调递减的过程,必须要把距离赋值给其中的一个,而二者关系无非三种要么大于,要么小于,要么等于。
如果赋给了大的,那么距离将变小,如果赋给了小的,距离有可能变小,也可能变大。如果相等为0,注意一旦出现为0的情况,将直接循环,距离永远等于一个值,而必须赋给其1,则要么0,d,要么就是d,d。
对于前面两种情况,如果是第一种,很显然是不是很像是辗转相除法?只不过这里是更相减损。所以如果大的减去小的的过程中出现了x,那么就成立。那么等价于(b-x)%a==0
而对于第二种情况,如果赋值给小的,那么第二次的距离,将会变成之前a,b的其中一个,那么必须将其赋值给其中一个,则要么变回原样,要么变成情况1的情况。
所以综上所述:只需要按照辗转相除法的过程模拟一下即可,只要在单调下降的过程出现即可。同时因为一旦为0,那么一定会出现循环。此时即可退出。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,x;
int T;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld%lld",&a,&b,&x);
bool flag=false;
if(a>b) swap(a,b);
if(a==x||b==x) {puts("YES");continue;}
while(a!=0&&b!=0)
{
if((b-x)%a==0&&(b-x)>=0){puts("YES");flag=true;break;}
else
{
ll tmp=a;
a=b%a,b=tmp;
}
}
if(!flag) puts("NO");
}
return 0;
}
E. Messages
https://codeforces.com/contest/1612/problem/E
题目大概是说有n个学生,每个人读k[i]条信,然后要读第m[i]封信,你可以把一堆信去pin一下。求期望。这一道题让我见识了概率题的新的写法。
t很小,只有20,而且对于大于20的部分,是相同的。
枚举即可。
将其从大到小排序,每一个的值,然后依次往下选即可。将所有的选择比较一下即可。
时间复杂度O(n^2logn)
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=2e5+52,K=20;
typedef long long ll;
typedef pair<int,int> PII;
int m[N],k[N],n;
bool frac_greater(pair<int, int> a, pair<int, int> b)//比较方式
{
return a.first * b.second > a.second * b.first;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d %d",&m[i],&k[i]);//每个人的情况,第几封信,读k封
vector<int> ans1;//储存每个人的情况
PII ans2={0,1};//储存结果
for(int i=1;i<=K;i++)//因为最多20,所以多余20计算会更小,多余20等于20
{
vector<int> score(N);//开N位,每个位为0
for(int j=0;j<n;j++) score[m[j]]+=min(i,k[j]);//得到每封信的情况如果选择这封信。(如果选了,且小则为1,否则则是k[j],没选则为0)
vector<PII > val;//桶排序
for(int j=0;j<N;j++) val.push_back({score[j],j});
sort(val.rbegin(),val.rend());
PII cur_ans={0,i};
vector<int> lt;
for(int j=0;j<i;j++)cur_ans.x+=val[j].x,lt.push_back(val[j].y);
if(frac_greater(cur_ans,ans2)) ans2=cur_ans,ans1=lt;
}
printf("%d\n",ans1.size());
for(auto x:ans1) cout<<x<<" ";
puts("");
return 0;
}
F. Armor and Weapons
https://codeforces.com/contest/1612/problem/F
F题是一道贪心题,大概是有两种东西,然后通过某种条件到达目标点。
第一反应:贪心,最短路(不知道为什么发现好多好多题目都可以直接挂到图论上,或者间接挂到图论上,最近的比赛看到了好多),DP最优解。
再一看,边权为1,BFS。
然后去看了一眼tutorial,好家伙就是这样。
直接看了一下唯一没能想到的是证明优化。
如果直接的BFS会是nm,这无法接受。
#include<bits/stdc++.h>
using namespace std;
int n, m;
#define x first
#define y second
typedef pair<int, int> comb;
comb norm(const comb& a)
{
return make_pair(min(a.x, n), min(a.y, m));
}
bool good(const comb& a)
{
return a.x == n || a.y == m;
}
bool comp(const comb& a, const comb& b)
{
if(a.x != b.x)
return a.x > b.x;
return a.y > b.y;
}
int main()
{
scanf("%d %d", &n, &m);
int v;
scanf("%d", &v);
set<comb> s;
for(int i = 0; i < v; i++)
{
int x, y;
scanf("%d %d", &x, &y);
s.insert(make_pair(x, y));
}
int steps = 0;
vector<comb> cur;
cur.push_back(make_pair(1, 1));
while(true)
{
if(cur[0] == make_pair(n, m))
break;
vector<comb> ncur;
for(auto x : cur)
{
int sum = x.x + x.y;
if(s.count(x))
sum++;
comb z = x;
z.x = sum;
ncur.push_back(norm(z));
z = x;
z.y = sum;
ncur.push_back(norm(z));
}
sort(ncur.begin(), ncur.end(), comp);
int mx = 0;
vector<comb> ncur2;
for(auto x : ncur)
{
if(x.y <= mx) continue;
mx = max(mx, x.y);
ncur2.push_back(x);
}
cur = ncur2;
steps++;
}
printf("%d\n", steps);
}
#include <bits/stdc++.h>
using namespace std;
const int LOG = 30;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
int q;
cin >> q;
vector<int> a(q), b(q);
for (int i = 0; i < q; i++){
cin >> a[i] >> b[i];
a[i]--;
b[i]--;
}
if (n > m){
swap(n, m);
for (int i = 0; i < q; i++){
swap(a[i], b[i]);
}
}
set<pair<int, int>> st;
for (int i = 0; i < q; i++){
st.insert(make_pair(a[i], b[i]));
}
vector<int> dp(n, -1);
dp[0] = 0;
int ans = 0;
while (true){
ans++;
vector<int> dp2(n, -1);
for (int i = 0; i < n; i++){
if (dp[i] != -1){
if (st.count(make_pair(i, dp[i])) == 0){
int x = min(i + dp[i] + 1, m - 1);
dp2[i] = max(dp2[i], x);
int y = min(i + dp[i] + 1, n - 1);
dp2[y] = max(dp2[y], dp[i]);
} else {
int x = min(i + dp[i] + 2, m - 1);
dp2[i] = max(dp2[i], x);
int y = min(i + dp[i] + 2, n - 1);
dp2[y] = max(dp2[y], dp[i]);
}
}
}
if (dp2[n - 1] == m - 1){
cout << ans << endl;
break;
}
swap(dp, dp2);
}
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
set<pair<int,int> > st;
int n,m,q;
inline int query(int u,int v)
{
return u+v+(st.find(make_pair(u,v))!=st.end());
}
int G(int x,int y)
{
x=min(x,n),y=min(y,m);
if(x==n&&y==m) return 0;
int q=query(x,y);
if(x==n) return G(x,q)+1;
if(y==m) return G(q,y)+1;
if(x>y) return G(x,q)+1;
return G(q,y)+1;
}
int F(int x,int y)
{
x=min(x,n),y=min(y,m);
if(x==n&&y==m) return 0;
int q=query(x,y),ans=0;
if(x==n) return F(x,q)+1;
if(y==m) return F(q,y)+1;
if(x>y)
{
ans=F(x,q);
if(x-y<=5) ans=min(ans,F(q,y));
else ans=min(ans,G(q,y));
return ans+1;
}
else
{
ans=F(q,y);
if(y-x<=5) ans=min(ans,F(x,q));
else ans=min(ans,G(x,q));
return ans+1;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=q;i++)
{
int u,v;
scanf("%d%d",&u,&v);
st.insert(make_pair(u,v));
}
printf("%d\n",F(1,1));
return 0;
}
G. Max Sum Array
https://codeforces.com/contest/1612/problem/G
考虑最外面的两个的贡献。每次做完一组切掉前后缀,继续做。
下面是一些证明
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=int(l);i<int(r);i++)
#define rep2(i,l,r) for(int i=int(l);i>int(r);i--)
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e6+52,mod=1e9+7;
template<class A, class B> ostream& operator <<(ostream& out, const pair<A, B> &p) {
return out << "(" << p.x << " " << p.y << ")";
}
template<class A> ostream& operator <<(ostream& out, const vector<A> &v) {
rep(i, 0, sz(v)) {
if(i) out << " ";
out << v[i];
}
return out;
}
ll m;
ll fact[N],cnt[N];
ll total,ans_sum,ans_num=1;
void init()
{
fact[0]=1;
for(int i=1;i<N;i++) fact[i]=fact[i-1]*1ll%mod*i%mod;
}
int main()
{
init();
scanf("%lld",&m);
rep(i,0,m)
{
ll x;scanf("%lld",&x);
cnt[x]++;total+=x;
}
rep2(i,N-50,1)//整形溢出
{
ans_num=(ans_num%mod*(fact[cnt[i]]%mod*fact[cnt[i]]%mod)%mod)%mod;
ans_sum=(ans_sum +(((i - 1)*cnt[i]%mod)*((total - cnt[i]) % mod)%mod))%mod;
total-=2*cnt[i],cnt[i-2]+=cnt[i];
}
ans_num=(ans_num*fact[cnt[1]])%mod;
cout<<ans_sum<<" "<<ans_num<<'\n';
return 0;
}
这里提供一下大佬的想法
Here is a slightly different approach for problem G which I think is easier. (Sorry if my explanation is not good.) It would seem that many other people have this approach too.
First, let's ask ourselves, given an array, how can we find the total sum of distances between all pairs of equal elements? For each element, we will need to add to the answer the sum of absolute values between each pair of indexes where it exists. This is a very well known problem, and can be easily understood by looking at an example.
Let's say an element exists at the indices [1,4,5,6,8][1,4,5,6,8]. Then, we will need to add (4−1)+(5−1)+(6−1)+(8−1)+(5−4)+(6−4)+(8−4)+(6−5)+(8−5)+(8−6)(4−1)+(5−1)+(6−1)+(8−1)+(5−4)+(6−4)+(8−4)+(6−5)+(8−5)+(8−6) to the answer. Overall, 1 has been subtracted 4 times, 4 has been subtracted twice, 5 does not contribute towards the sum, 6 is added twice, and 8 is added 4 times. So we will add to the sum (−4)∗1+(−2)∗4+(0)∗5+(2)∗6+(4)∗8(−4)∗1+(−2)∗4+(0)∗5+(2)∗6+(4)∗8. In general, for a sorted array [p1,p2,…,px][p1,p2,…,px], we will add to the answer p1∗(1−x)+p2∗(3−x)+p3∗(5−x)+...+px∗(x−1)p1∗(1−x)+p2∗(3−x)+p3∗(5−x)+...+px∗(x−1). We can think of this as assigning multiplying each index by a coefficient and finding the total sum of indexes, such that the coefficients assigned to indexes with the same element are a [1−x,3−x,…,x−1][1−x,3−x,…,x−1] in ascending order.
We can now move on to maximising the answer. We will generate a coefficient array. For each cici, we will add the elements [1−ci,3−ci,…,ci−1][1−ci,3−ci,…,ci−1] to the coefficient array. Then, we want to obtain a permutation of this coefficient array [p1,p2,…,pn][p1,p2,…,pn] such that ∑ni=1ipi∑i=1nipi is maximised. By the rearrangement theorem, this sum is maximised when pp is sorted, and it is easy to see that such a permutation is possible.
For more clarity, consider the case where c=[3,3,2]c=[3,3,2]. We will generate the coefficient array [−2,0,2]+[−2,0,2]+[−1,1]=[−2,−2,−1,0,0,1,2,2][−2,0,2]+[−2,0,2]+[−1,1]=[−2,−2,−1,0,0,1,2,2]. Then the maximum answer will be (−2)∗0+(−2)∗1+(−1)∗2+(0)∗3+(0)∗4+(1)∗5+(2)∗6+(2)∗7=27(−2)∗0+(−2)∗1+(−1)∗2+(0)∗3+(0)∗4+(1)∗5+(2)∗6+(2)∗7=27, and this is achievable for example by choosing a=[1,2,3,1,2,3,1,2]a=[1,2,3,1,2,3,1,2].
Now, how do we do this fast? Instead of actually generating the coefficient array, we will simply create a frequency map storing how many times each element exists in the coefficient array. We can create this map quickly using a difference array (or you can visualise this as a sweepline). We will then iterate through this map in ascending order. For each element ee which occurs numnum times in the coefficient array, we will assign ee as the coefficient of the numnum lowest indexes which we haven't assigned yet, and increment our answer by (e∗e∗ sum of chosen indexes). Remember that of the distinct elements in aa, exactly numnum of these elements will have contributed ee to the coefficient array, so these indexes will correspond to some permutation of these numnum elements in aa. We will therefore multiply the number of possible arrays by num!num!, as this is the number of permutation of these numnum elements in aa.
See https://codeforces.com/contest/1612/submission/136599117 for implementation details. If an array is used instead of a map, the overall complexity of this algorithm is O(m+max(ci))O(m+max(ci)).

浙公网安备 33010602011771号