codeforces Educational Codeforces Round 24 (A~F)
题目链接:http://codeforces.com/contest/818
A. Diplomas and Certificates
题解:水题
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
int main() {
ll n , k;
cin >> n >> k;
ll gg = n / 2;
if(gg < (k + 1)) cout << 0 << ' ' << 0 << ' ' << n << endl;
else {
cout << gg / (k + 1) << ' ' << gg / (k + 1) * k << ' ' << n - gg / (k + 1) - gg / (k + 1) * k << endl;
}
return 0;
}
B. Permutation Game(构造)
题解:也是一道水题模拟构造一下就行了
if l[i + 1] > l[i] a[l[i]] = l[i + 1] - l[i];
else a[l[i]] = l[i + 1] - l[i] + n
最后没有被赋值的点敷一下值就行。
#include <iostream>
using namespace std;
int l[200] , a[200] , vis[200];
int main() {
int n , m;
cin >> n >> m;
int flag = 0;
for(int i = 1 ; i <= m ; i++) cin >> l[i];
for(int i = 1 ; i <= n ; i++) a[i] = 0;
for(int i = 1 ; i < m ; i++) {
if(l[i + 1] > l[i]) {
if(a[l[i]]) {
if(a[l[i]] != l[i + 1] - l[i]) {
flag = 1;
break;
}
}
a[l[i]] = l[i + 1] - l[i];
}
else {
if(a[l[i]]) {
if(a[l[i]] != l[i + 1] - l[i] + n) {
flag = 1;
break;
}
}
a[l[i]] = l[i + 1] - l[i] + n;
}
}
for(int i = 1 ; i <= n ; i++) {
vis[a[i]]++;
if(!a[i]) continue;
if(vis[a[i]] > 1) {
flag = 1;
break;
}
}
int cnt = 1;
for(int i = 1 ; i <= n ; i++) {
if(a[i] == 0) {
while(vis[cnt] != 0) cnt++;
a[i] = cnt;
vis[a[i]] = 1;
}
}
for(int i = 1 ; i <= n ; i++) if(!a[i]) {flag = 1; break;}
if(flag) cout << -1 << endl;
else {
for(int i = 1 ; i <= n ; i++) cout << a[i] << ' ';
cout << endl;
}
return 0;
}
C. Sofa Thief
题解:稍微有点麻烦的题目,主要还是求一下前缀,由于一个沙发占两格所以处理起来可能有点麻烦具体看一下代码最终还是要求前缀来解决这个问题。
#include <iostream>
#include <cstring>
using namespace std;
const int M = 1e5 + 10;
struct TnT {
int x1 , y1 , x2 , y2 , id;
}T[M];
int l[M] , r[M] , t[M] , b[M];
int sum[M];
int main() {
int d;
cin >> d;
int n , m;
cin >> n >> m;
for(int i = 1 ; i <= d ; i++) {
cin >> T[i].x1 >> T[i].y1 >> T[i].x2 >> T[i].y2 , T[i].id = i;
}
int cntl , cntr , cntt , cntb;
cin >> cntl >> cntr >> cntt >> cntb;
memset(sum , 0 , sizeof(sum));
for(int i = 1 ; i <= d ; i++) {
sum[min(T[i].x1 , T[i].x2)]++;
}
for(int i = 1 ; i <= n ; i++) {
sum[i] += sum[i - 1];
}
for(int i = 1 ; i <= d ; i++) {
if(T[i].x1 != T[i].x2) l[i] = sum[min(T[i].x1 , T[i].x2)] - 1;
else l[i] = sum[T[i].x1 - 1];
}
memset(sum , 0 , sizeof(sum));
for(int i = 1 ; i <= d ; i++) {
sum[max(T[i].x1 , T[i].x2)]++;
}
for(int i = n ; i >= 1 ; i--) {
sum[i] += sum[i + 1];
}
for(int i = 1 ; i <= d ; i++) {
if(T[i].x1 != T[i].x2) r[i] = sum[max(T[i].x1 , T[i].x2)] - 1;
else r[i] = sum[T[i].x1 + 1];
}
memset(sum , 0 , sizeof(sum));
for(int i = 1 ; i <= d ; i++) {
sum[min(T[i].y1 , T[i].y2)]++;
}
for(int i = 1 ; i <= m ; i++) {
sum[i] += sum[i - 1];
}
for(int i = 1 ; i <= d ; i++) {
if(T[i].y1 != T[i].y2) t[i] = sum[min(T[i].y1 , T[i].y2)] - 1;
else t[i] = sum[T[i].y1 - 1];
}
memset(sum , 0 , sizeof(sum));
for(int i = 1 ; i <= d ; i++) {
sum[max(T[i].y1 , T[i].y2)]++;
}
for(int i = m ; i >= 1 ; i--) {
sum[i] += sum[i + 1];
}
for(int i = 1 ; i <= d ; i++) {
if(T[i].y1 != T[i].y2) b[i] = sum[max(T[i].y1 , T[i].y2)] - 1;
else b[i] = sum[T[i].y1 + 1];
}
int flag = 0;
for(int i = 1 ; i <= d ; i++) {
//cout << l[i] << ' ' << r[i] << ' ' << t[i] << ' ' << b[i] << endl;
if(cntl == l[i] && cntr == r[i] && cntt == t[i] && cntb == b[i]) {
flag = 1;
cout << i << endl;
break;
}
}
if(!flag) cout << -1 << endl;
return 0;
}
D. Multicolored Cars (优先队列)
题解:可用优先队列辅助一下具体看一下代码挺好理解的。一旦出现出现次数最小的颜色小于A直接pop出去。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int M = 1e6 + 10;
int cnt[M] , vis[M];
struct TnT {
int num;
TnT() {}
TnT(int num):num(num) {}
bool operator <(const TnT &a) const {
return cnt[num] > cnt[a.num];
}
};
priority_queue<TnT> q;
int main() {
int n , A;
scanf("%d%d" , &n , &A);
int count = 0 , flag = 1;
memset(vis , 0 , sizeof(vis));
for(int i = 1 ; i <= n ; i++) {
int c;
scanf("%d" , &c);
if(c == A) count++;
else {
cnt[c]++;
if(cnt[c] > count && !vis[c]) q.push(c) , vis[c] = 1;
else vis[c] = 1;
}
while(!q.empty()) {
TnT gg = q.top();
if(cnt[gg.num] >= count) break;
q.pop();
}
if(q.empty()) flag = 0;
}
if(!flag) printf("-1\n");
else {
if(q.empty()) printf("-1\n");
else printf("%d\n" , q.top().num);
}
return 0;
}
E. Card Game Again(思维+二分)
题解:现将k因式分解然后拿因式的和求前缀显然是递增的然后遍历一遍向前二分
#include <iostream>
#include <cstring>
using namespace std;
const int M = 1e5 + 10;
typedef long long ll;
ll gcd(ll a , ll b) {
return (b <= 0) ? a : gcd(b , a % b);
}
struct dig {
ll num;
int sum;
};
struct TnT {
int Size;
dig a[64];
TnT():Size(0) {
memset(a , 0 , sizeof(a));
}
TnT operator +=(const TnT &b) {
for(int i = 0 ; i < 64 ; i++) {
a[i].sum += b.a[i].sum;
}
return (*this);
}
TnT operator -(const TnT &b) {
for(int i = 0 ; i < 64 ; i++) {
a[i].sum -= b.a[i].sum;
}
return (*this);
}
}s , pre[M];
void getTnT(ll x , TnT &gg) {
gg.a[gg.Size].num = 1 , gg.a[gg.Size].sum++;
gg.Size++;
ll i;
for(i = 2 ; i * i <= x ; i++) {
if(x % i == 0) {
gg.a[gg.Size].num = i;
while(!(x % i)) x /= i , gg.a[gg.Size].sum++;
gg.Size++;
}
}
if(x >= i) gg.a[gg.Size].num = x , gg.a[gg.Size].sum++ , gg.Size++;
}
void getTnT_arr(ll x , TnT &gg) {
int len = s.Size;
gg.a[0].num = 1 , gg.a[0].sum++;
for(int i = 1 ; i < len ; i++) {
if(x % s.a[i].num == 0) {
gg.a[i].num = s.a[i].num;
while(!(x % s.a[i].num)) x /= s.a[i].num , gg.a[i].sum++;
}
else gg.a[i].num = s.a[i].num , gg.a[i].sum = 0;
}
gg.Size = len;
}
ll arr[M];
int cau(int pos , int ed) {
TnT gl = pre[ed];
TnT gg = gl - pre[pos - 1];
int flag = 0;
for(int i = 0 ; i < s.Size ; i++) {
if(gg.a[i].sum < s.a[i].sum) {
flag = 1;
break;
}
}
return flag;
}
int binsearch(int l , int r , int ed) {
int mid = (l + r) >> 1;
int ans = 0;
while(l <= r) {
mid = (l + r) >> 1;
if(cau(mid , ed)) r = mid - 1;
else {
ans = mid;
l = mid + 1;
}
}
return ans;
}
int main() {
int n;
ll k;
scanf("%d%lld" , &n , &k);
getTnT(k , s);
pre[0].Size = s.Size;
for(int i = 1 ; i <= n ; i++) {
scanf("%lld" , &arr[i]);
arr[i] = gcd(arr[i] , k);
getTnT_arr(arr[i] , pre[i]);
pre[i] += pre[i - 1];
}
ll ans = 0;
for(int i = 1 ; i <= n ; i++) {
int pos = binsearch(1 , i , i);
ans += pos;
}
printf("%lld\n" , ans);
return 0;
}
F. Level Generation(三分)
题解:主要还是推一下公式一共有n个点,有k个点是任意联通的那么显然有n-k个点是单连通的那么n-k个单连通的点贡献的边数是(n-k)条边。k个点最多能连接的边的个数是min(n-k(由于要求桥的个数要超过总边数的一半),k*(k-1)/2(k个点最多能连接的边的个数)),这个自行理解一下。然后我们可以发现边的贡献是
n-k+min(n-k,k*(k-1)/2)。像这种有单峰或单谷的函数可以利用三分来求最值
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
ll n;
bool cau(ll k) {
if(k * (k - 1) / 2 > n - k) return true;
else return false;
}
ll ternsearch(ll l , ll r) {
ll mid = (l + r) >> 1;
ll sum = 0;
if(cau(mid)) sum = 2 * n - 2 * mid;
else sum = n - mid + mid * (mid - 1) / 2;
while(l <= r) {
mid = (l + r) >> 1;
ll midl = (l + mid) >> 1;
ll midr = (r + mid) >> 1;
ll now = 0 , sum1 = 0 , sum2 = 0;
if(cau(mid)) now = 2 * n - 2 * mid;
else now = n - mid + mid * (mid - 1) / 2;
sum = max(sum , now);
if(cau(midl)) sum1 = 2 * n - 2 * midl;
else sum1 = n - midl + midl * (midl - 1) / 2;
sum = max(sum1 , sum);
if(cau(midr)) now = 2 * n - 2 * midr;
else now = n - midr + midr * (midr - 1) / 2;
sum = max(sum2 , sum);
if(cau(midl) && cau(midr)) r = mid - 1;
else if(!cau(midl) && !cau(midr)) l = mid + 1;
else {
if(sum1 < sum2) r = midr - 1;
else l = midl + 1;
}
}
return sum;
}
int main() {
int q;
scanf("%d" , &q);
while(q--) {
scanf("%lld" , &n);
printf("%lld\n" , ternsearch(1 , n));
}
return 0;
}

浙公网安备 33010602011771号