今天状态还不错,补题补得比较顺利,div2 Solved:5 out of 6
A.
将b,c作为一个整体,个数为b,c的较小值;然后根据e,f的大小,决定先买a,还是先买b,c这个整体。如果先买了,就要贪心的尽可能买光。
#include <bits/stdc++.h>
using namespace std;
int a, b, c, d, e, f, ans1, ans2, sum;
int main ( ) {
scanf ( "%d%d%d%d%d%d" , & a, & b, & c, & d, & e, & f) ;
b= min ( b, c) ;
if ( e< f)
{
ans2= min ( b, d) ;
d- = ans2;
sum+ = ans2* f;
ans1= min ( a, d) ;
sum+ = ans1* e;
}
else
{
ans1= min ( a, d) ;
d- = ans1;
sum+ = ans1* e;
ans2= min ( b, d) ;
sum+ = ans2* f;
}
printf ( "%d\n" , sum) ;
return 0 ;
}
B.
构造一种方案使得整一行的颜色均相同,要求改变次数<=3n。
用一种改变次数<=2n的做法很方便:先假设最终整行都变成了第一个格子的颜色,那么对于【2,n-1】的格子来说,如果当前颜色和第一个格子颜色不同,就改变这个格子和后面的格子颜色,这样操作后可以保证【1,n-1】的格子颜色均相同了。
现在有3种情况:
1.第n个格子颜色恰好相同,结束操作;
2.n-1是一个偶数,那么就把【1,n-1】的格子每隔一个染色,染成和第n个格子一样的颜色;
3.n-1是一个奇数,无法完成染色。
#include <bits/stdc++.h>
using namespace std;
const int N= 2e2 + 5 ;
int n, tot;
int a[ N] , ans[ N] ;
char str[ N] ;
int main ( ) {
scanf ( "%d" , & n) ;
scanf ( "%s" , str+ 1 ) ;
for ( register int i= 1 ; i<= n; ++ i) if ( str[ i] == 'B' ) a[ i] = 1 ;
for ( register int i= 2 ; i< n; ++ i)
if ( a[ i] != a[ 1 ] )
{
a[ i] ^ = 1 ; a[ i+ 1 ] ^ = 1 ;
ans[ ++ tot] = i;
}
if ( a[ n] != a[ 1 ] && ( n- 1 ) % 2 == 1 ) printf ( "%d\n" , - 1 ) ;
else
{
if ( a[ n] != a[ 1 ] )
{
for ( register int i= 1 ; i<= n- 1 ; i+ = 2 ) ans[ ++ tot] = i;
}
printf ( "%d\n" , tot) ;
for ( register int i= 1 ; i<= tot; ++ i) printf ( "%d " , ans[ i] ) ;
}
return 0 ;
}
C.
将平面根据学校的位置分成8个部分:
分别为:在学习左上,左下,右上,右下,正左,正右方,正上,正下。
可以发现,如果帐篷放在(x,y+1),那么左上,右上,正上的学生可以买到帐篷;同理可得放在(x,y-1),(x+1,y),(x-1,y)。
把这四个值取一个最大值即可。
#include <bits/stdc++.h>
using namespace std;
const int N= 2e5 + 5 ;
int n, xx, yy, ans1, ans2, ans3, ans4, sum1, sum2, sum3, sum4, ans, ansx, ansy;
int x[ N] , y[ N] ;
int main ( ) {
scanf ( "%d%d%d" , & n, & xx, & yy) ;
for ( register int i= 1 ; i<= n; ++ i) scanf ( "%d%d" , & x[ i] , & y[ i] ) ;
for ( register int i= 1 ; i<= n; ++ i)
{
if ( x[ i] < xx && y[ i] < yy) ans1++ ;
if ( x[ i] < xx && y[ i] > yy) ans2++ ;
if ( x[ i] > xx && y[ i] < yy) ans3++ ;
if ( x[ i] > xx && y[ i] > yy) ans4++ ;
if ( x[ i] == xx && y[ i] < yy) sum1++ ;
if ( x[ i] == xx && y[ i] > yy) sum2++ ;
if ( x[ i] < xx && y[ i] == yy) sum3++ ;
if ( x[ i] > xx && y[ i] == yy) sum4++ ;
}
ans= max ( ans, ans1+ ans2+ sum3) ;
ans= max ( ans, ans1+ ans3+ sum1) ;
ans= max ( ans, ans3+ ans4+ sum4) ;
ans= max ( ans, ans2+ ans4+ sum2) ;
if ( ans== ans1+ ans2+ sum3) ansx= xx- 1 , ansy= yy;
if ( ans== ans1+ ans3+ sum1) ansx= xx, ansy= yy- 1 ;
if ( ans== ans3+ ans4+ sum4) ansx= xx+ 1 , ansy= yy;
if ( ans== ans2+ ans4+ sum2) ansx= xx, ansy= yy+ 1 ;
printf ( "%d\n" , ans) ;
printf ( "%d %d\n" , ansx, ansy) ;
return 0 ;
}
D.
一种方法是贪心地做背包dp,既然要贪心了,为何还要贪心地做dp呢?一贪到底不好吗?
所以我写了可反悔贪心。
此题可以贪心,是基于每个城市只需要一个士兵来驻守,如果需要ith个士兵来驻守,就不能贪心了。
由于无论是早驻守还是晚驻守,收益均相同,所以当然是晚驻守会使得更容易通关,我们统计出每个城市能够被驻守的最晚时间,然后决定:不到最后一刻,坚决不驻守士兵。
弄清楚了这个原则后,就是一道基础的可反悔贪心了,用一个小根堆维护即可。
#include <bits/stdc++.h>
using namespace std;
const int N= 5e3 + 5 , M= 3e5 + 5 ;
int n, m, k, u, v, ans;
int a[ N] , b[ N] , c[ N] , maxn[ N] ;
int cnt, head[ N] ;
struct edge{ int next, to; } e[ M] ;
inline void add ( int u, int v)
{
cnt++ ;
e[ cnt] . next= head[ u] ;
e[ cnt] . to= v;
head[ u] = cnt;
}
priority_queue< int , vector< int > , greater< int > > q;
int main ( ) {
scanf ( "%d%d%d" , & n, & m, & k) ;
for ( register int i= 1 ; i<= n; ++ i) scanf ( "%d%d%d" , & a[ i] , & b[ i] , & c[ i] ) ;
for ( register int i= 1 ; i<= n; ++ i) maxn[ i] = i;
for ( register int i= 1 ; i<= m; ++ i)
{
scanf ( "%d%d" , & u, & v) , add ( u, v) ;
maxn[ v] = max ( maxn[ v] , u) ;
}
for ( register int i= 1 ; i<= n; ++ i)
{
while ( k< a[ i] )
{
if ( q. size ( ) )
{
int u= q. top ( ) ; q. pop ( ) ;
ans- = u;
k++ ;
}
else
{
puts ( "-1" ) ;
return 0 ;
}
}
k+ = b[ i] ;
if ( maxn[ i] == i)
{
if ( k) k-- , ans+ = c[ i] , q. push ( c[ i] ) ;
else if ( q. size ( ) )
{
if ( c[ i] > q. top ( ) )
{
ans- = q. top ( ) , q. pop ( ) ;
ans+ = c[ i] , q. push ( c[ i] ) ;
}
}
}
for ( register int j= head[ i] ; j; j= e[ j] . next)
if ( maxn[ e[ j] . to] == i)
{
if ( k) k-- , ans+ = c[ e[ j] . to] , q. push ( c[ e[ j] . to] ) ;
else if ( q. size ( ) )
{
if ( c[ e[ j] . to] > q. top ( ) )
{
ans- = q. top ( ) , q. pop ( ) ;
ans+ = c[ e[ j] . to] , q. push ( c[ e[ j] . to] ) ;
}
}
}
}
printf ( "%d\n" , ans) ;
return 0 ;
}
E.
这个函数变来变去,好像在数学中总是出现。
一眼看去,答案x具有单调性,所以二分枚举,现在就需要我们迅速统计出当答案为x时再【1,n】的范围内有多少符合条件的数字。
找了一波规律以后才发现,对于奇数和偶数的统计是不同的,原来,奇数有奇数的单调性,偶数有偶数的单调性。
修正刚刚的结论,把奇偶分开做两次二分答案取max即可。
统计符合条件的数字,我用了log(n)的复杂度,如果想更加优化的话,还可以套个二分,这个理论复杂度会变成log(log(n)),但是实际常数应该挺大的。
本人代码复杂度:log(n)*log(n);理论最优复杂度:log(n)*log(log(n))。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, k, l, r, mid, ans1, ans2;
signed main ( ) {
scanf ( "%lld%lld" , & n, & k) ;
l= 1ll ; r= n/ 2ll ;
while ( l<= r)
{
mid= l+ r>> 1ll ;
int now= mid* 2ll ;
int i= 0ll ;
int sum= 0ll ;
while ( ( now<< i) + ( 1ll << ( i+ 1ll ) ) - 1ll <= n) sum+ = ( 1ll << ( i+ 1ll ) ) , i++ ;
if ( ( now<< i) <= n) sum+ = n- ( now<< i) + 1ll ;
if ( sum>= k) ans1= now, l= mid+ 1ll ;
else r= mid- 1ll ;
}
l= 1ll ; if ( n% 2ll == 1ll ) r= n/ 2ll + 1 ; else r= n/ 2ll ;
while ( l<= r)
{
mid= l+ r>> 1ll ;
int now= mid* 2ll - 1ll ;
int i= 0ll ;
int sum= 0ll ;
while ( ( now<< i) + ( 1ll << i) - 1ll <= n) sum+ = ( 1ll << i) , i++ ;
if ( ( now<< i) <= n) sum+ = n- ( now<< i) + 1ll ;
if ( sum>= k) ans2= now, l= mid+ 1ll ;
else r= mid- 1ll ;
}
printf ( "%lld\n" , max ( ans1, ans2) ) ;
return 0 ;
}
F.
待小蒟蒻去看看题解…