牛客寒假算法基礎集訓營6 解題報告
前言
離ak最近的1場qwq
寫了9題,再給我5min就能ak的
混了個rk20多qwq
怎麽天天出原題啊我做過的都有3道了
A
做法 : 小學奧數
小學奧數題吧...
只要你智商在線,人腦裏模擬一下不就行了
顯然也就那麽幾種情況。
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define N 10000100 ll n, m; int main() { scanf("%lld%lld", &n, &m); if(n > m * 9 || n < m * 6) return puts("jgzjgzjgz"), 0; ll c = n - m * 6; if(c > m) puts("0"); else printf("%lld\n", m - c); return 0; }
B
做法:暴力/二分
一眼秒是二分。
具體做法是二分天數,然後等差數列求和判斷。
但是寫掛了不知道是溢出了還是什麽。賽後交了是90分。
最後一個大爺告訴我暴力能過...
怪不得是全場通過最多
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; #define N 5000050 ll n, m, d, x; bool check(ll x) { __int128 s = (__int128)(x * d + n + n) * x / 2; return s >= (__int128)m; } int main() { scanf("%lld%lld%lld%lld", &n, &m, &d, &x); ll ans = 0, res = 0; while(res < m) { res += n; n += d; ans++; } printf("%lld\n", ans); }
C
做法:貪心
因為沒啥特殊性質,顯然直接按塗上去的價值排序就好,然後取前n大的價值即可
是個sb題
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define N 10000100 int n, m; struct Node { int a, b; } a[N]; bool operator < (Node a, Node b) { return a.b > b.b; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i) scanf("%d", &a[i].a); for(int i = 1; i <= m; ++i) scanf("%d", &a[i].b); sort(a + 1, a + m + 1); ll ans = 0; for(int i = 1; i <= m; ++i) { if(n >= a[i].a) { ans += 1ll * a[i].a * a[i].b; n -= a[i].a; } else { // if(!n) break; ans += 1ll * a[i].b * n; break; } } printf("%lld\n", ans); return 0; }
D
做法:貪心
之前做過類似的題,貌似是洛谷月賽。
絕對值小於等於1,很麻煩,所以不如欽定每一堆只要能拿就一定拿完,如果有一個要湊的就從右邊拿。
這個正確性挺顯然的吧,如果不是很理解可以把樣例畫出來自己看看
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 5000050
int n;
ll a[N];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
ll ans = 0;
for(int i = 1; i <= n; ++i) {
ans += a[i] / 2;
if(a[i] % 2 == 0) continue;
if(a[i + 1] == 0) continue;
ans++; a[i + 1]--;
}
printf("%lld\n", ans);
}
E
做法:二維前綴和
sb題啊。。。
一開始以為是個神仙題。我看錯了題意以為d是每次給定的,結果是先給的。。。
那麽就和NOIP2016組合數問題基本一樣了
怎麽天天出原題啊
預處理\(sum[i][j]\)表示前\([i,j]\)個格子,滿足值大於等於d的有多少個。
那麽按二維前綴和的思路容斥一下就好。如果不了解二維前綴和的看一下下面這個式子也就懂了
\(sum[l2][r2] + sum[l1 - 1][r1 - 1] - sum[l1 - 1][r2] - sum[l2][r1 - 1]\)
實在不行再畫個圖?那圖網上找找就好,爛大街的圖
至於\(n*m<1e6\)開個vector就好
據說卡空間,不過我是沒給卡到
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <deque>
#include <map>
#include <set>
#define ll long long
#define inf 0x3f3f3f3f
#define il inline
namespace io {
#define in(a) a=read()
#define out(a) write(a)
#define outn(a) out(a),putchar('\n')
#define I_int ll
inline I_int read() {
I_int x = 0 , f = 1 ; char c = getchar() ;
while( c < '0' || c > '9' ) { if( c == '-' ) f = -1 ; c = getchar() ; }
while( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar() ; }
return x * f ;
}
char F[ 200 ] ;
inline void write( I_int x ) {
if( x == 0 ) { putchar( '0' ) ; return ; }
I_int tmp = x > 0 ? x : -x ;
if( x < 0 ) putchar( '-' ) ;
int cnt = 0 ;
while( tmp > 0 ) {
F[ cnt ++ ] = tmp % 10 + '0' ;
tmp /= 10 ;
}
while( cnt > 0 ) putchar( F[ -- cnt ] ) ;
}
#undef I_int
}
using namespace io ;
using namespace std ;
#define N 1000100
int n, m, d;
vector<int>sum[N];
int main() {
n = read(), m = read(), d = read();
for(int i = 1; i <= n; ++i) sum[i].push_back(0); for(int i = 0; i <= m; ++i) sum[0].push_back(0);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
int x = read();
x < d ? sum[i].push_back(0) : sum[i].push_back(1);
}
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
}
}
int Q = read();
while(Q--) {
int l1 = read(), r1 = read(), l2 = read(), r2 = read();
int ans = sum[l2][r2] + sum[l1 - 1][r1 - 1] - sum[l1 - 1][r2] - sum[l2][r1 - 1];
printf("%d\n", ans);
}
return 0;
}
F
做法:搜索+構造
唯一的難題。
大致做法是寬搜一下,因為其實合法的構造方案很少。
結束了就棄了沒寫,這題具體做法還是看看官方題解吧。。。
G
做法:按位貪心
挺顯然的一道題,3min秒了,然後因為上界搞錯調了要1h。
或的規則是有1則1,那麽考慮每一位,如果\(b-a\)(即a到b變換了的位數,這中間肯定有1)比這一位大,那麽顯然就會有一個1跟他或一下,那麽這一位就固定為1了
上界要枚舉到62...我上界試了31,32,63都掛了...最後62就過了,有毒
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 5000050
ll a, b;
int main() {
while(~scanf("%lld%lld", &a, &b)) {
ll ans = b | a;
for(ll k = 63; k >= 0; k--) {
if((ll)(1ll << (k)) <= (b - a)) ans |= (1ll << (k));
}
printf("%lld\n", ans);
}
}
/*
枚舉每一位
對於一位k,如果(a-b)>=(1<<k),那麽這一位就可以|1
*/
H
做法:暴力枚舉+線段樹優化
這題做了好久...做完這題,F時間就不夠了...
一開始沒啥思路,然後蔡隊在牛客群上說了:“H有啥難的,暴力枚舉啊”
orz。
然後往這方面想了想就想出來了。
顯然比較麻煩的就是這個全體右移k位。
如果我們知道了一共要右移x次,那麽其實每頭豬的最小代價其實是確定了的,就是\(min(a_i,min(a_{i-x}...a_{i-1})+x)\)(這是因為我們可以安排放入這頭豬的時間,比如我們要取\(i-2\)這個地方的代價來填\(i\)這個位置,那麽我們在剩下兩次右移次數的時候放入\(i-2\)這個地方就好了。如果要用原來的價值,那麽全部右移完後再放進去就好)
所以現在就明朗了。
枚舉右移次數k,對於每個k算出最小代價,使用數據結構求區間最小值就可以了。
我一開始st表寫掛了。。。爆了4發。最後用了線段樹過了。
至於n右移了會到1的,可以分類討論一下,也可以直接斷環成鏈。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100010
const ll inf = 1e18;
#define int long long
int n, x;
int a[N];
struct tree{
int l,r,mn;
}t[N<<2];
#define mid ((l + r) >> 1)
#define lc (rt << 1)
#define rc (rt << 1 | 1)
void pushup(int rt) {
t[rt].mn = min(t[lc].mn, t[rc].mn);
}
void build(int l, int r, int rt) {
t[rt].l = l; t[rt].r = r;
if(l == r) {
t[rt].mn = a[l];
return;
}
build(l, mid, lc); build(mid + 1, r, rc); pushup(rt);
}
#define l t[rt].l
#define r t[rt].r
int query(int L, int R, int rt) {
if(L <= l && r <= R) {
return t[rt].mn;
}
int ans = inf;
if(L <= mid) ans = min(ans, query(L, R, lc));
if(R > mid) ans = min(ans, query(L, R, rc));
return ans;
}
#undef l
#undef r
#undef mid
#undef lc
#undef rc
signed main() {
scanf("%lld%lld", &n, &x);
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
build(1, n, 1);
ll ans = 1e18;
for(int k = 0; k <= n; ++k) {
ll sum = k * x;
for(int i = 1; i <= n; ++i) {
if(i - k < 1) sum += min(query(1, i, 1), query(n - k + i, n, 1));
else sum += query(i - k, i, 1);
}
ans = min(ans, sum);
}
printf("%lld\n", ans);
return 0;
}
I
棧(括號序列)
本質上就是個括號序列
因為註意到每次最多只能+10分,最少+5分(+0我們肯定不會去選他)。
而且每個得分需要的次數是一樣的。於是我們有一個貪心的做法。每次把當前數和棧頂比較,如果可以匹配則彈出並將答案+10,如果不行就扔進棧裏。
最後把棧裏的兩兩彈出並+5即可
抽象一下,就是個括號序列匹配而已
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 1000010
char s[N];
int st[N];
int main() {
scanf("%s", s + 1);
int ans = 0, n = strlen(s + 1), top = 0;
for(int i = 1; i <= n; ++i) {
if(!top) {
st[++top] = s[i] - '0';
continue;
}
if(s[i] - '0' == st[top]) {
top--;
ans += 10;
} else {
st[++top] = s[i] - '0';
}
}
while(top) {
top -= 2;
ans += 5;
}
printf("%d\n", ans);
}
J
做法:搜索
直接搜就行了。。。
因為是原題所以我就懶得寫了,我拿了我以前寫過的那題的代碼交了,用的spfa。
原題是CF1064D
http://codeforces.com/contest/1064/problem/D
正好我當時打過。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define debug printf("233\n")
#define inf 0x3f3f3f3f
#define il inline
#define in1(a) read(a)
#define in2(a,b) in1(a),in1(b)
#define in3(a,b,c) in2(a,b),in1(c)
#define in4(a,b,c,d) in2(a,b),in2(c,d)
inline void read( int &x ){
x = 0 ; int f = 1 ; char c = getchar() ;
while( c < '0' || c > '9' ) {
if( c == '-' ) f = -1 ;
c = getchar() ;
}
while( c >= '0' && c <= '9' ) {
x = (x << 1) + (x << 3) + c - 48 ;
c = getchar() ;
}
x *= f ;
}
inline void readl( ll &x ){
x = 0 ; ll f = 1 ; char c = getchar() ;
while( c < '0' || c > '9' ) {
if( c == '-' ) f = -1 ;
c = getchar() ;
}
while( c >= '0' && c <= '9' ) {
x = (x << 1) + (x << 3) + c - 48 ;
c = getchar() ;
}
x *= f ;
}
using namespace std ;
#define N 2010
int n , m , r , c , x , y ;
char ch[ N ][ N ] ;
struct edge {
int to , nxt , v ;
} e[ N * N * 4 ] , E[ N * N * 4 ];
int head[ N * N ] , cnt , Head[ N * N ] , Cnt ;
int vis[ N * N ] , d[ N * N ] , Vis[ N * N ] , D[ N * N ] ;
int q[ 1000100 ] ;
void ins1( int u , int v , int w ) {
e[ ++ cnt ].to = v ;
e[ cnt ].nxt = head[ u ] ;
e[ cnt ].v = w ;
head[ u ] = cnt ;
}
void ins2( int u , int v , int w ) {
E[ ++ Cnt ].to = v ;
E[ Cnt ].nxt = Head[ u ] ;
E[ Cnt ].v = w ;
Head[ u ] = Cnt ;
}
void spfa() {
int s = (r-1) * m + c ;
q[ 1 ] = s ;
int l = 1 , r = 2 ;
for( int i = 1 ; i <= n * m ; i ++ ) d[ i ] = inf ;
d[ s ] = 0 ;
vis[ s ] = 1 ;
while( l != r ) {
int u = q[ l ++ ] ;
vis[ u ] = 0 ;
if( l == 1000000 ) l = 1 ;
for( int i = head[ u ] ; i ; i = e[ i ].nxt ) {
int v = e[ i ].to ;
if( d[ v ] > d[ u ] + e[ i ].v ) {
d[ v ] = d[ u ] + e[ i ].v ;
if( !vis[ v ] ) {
vis[ v ] = 1 ;
q[ r ++ ] = v ;
if( r == 1000000 ) r = 1 ;
}
}
}
}
}
void spfa2() {
int s = (r-1) * m + c ;
int l = 1 , r = 2 ;
q[ 1 ] = s ;Vis[ s ] = 1 ;
for( int i = 1 ; i <= n * m ; i ++ ) D[ i ] = inf ;
D[ s ] = 0 ;
while( l != r ) {
int u = q[ l ++ ] ;
Vis[ u ] = 0 ;
if( l == 1000000 ) l = 1 ;
for( int i = Head[ u ] ; i ; i = E[ i ].nxt ) {
int v = E[ i ].to ;
if( D[ v ] > D[ u ] + E[ i ].v ) {
D[ v ] = D[ u ] + E[ i ].v ;
if( !Vis[ v ] ) {
Vis[ v ] = 1 ;
q[ r ++ ] = v ;
if( r == 1000000 ) r = 1 ;
}
}
}
}
}
int main(){
in2( n , m ) ;
in2( r , c ) ;
in2( x , y ) ;
for( int i = 1 ; i <= n ; i ++ ) {
scanf( "%s" , ch[ i ] + 1 ) ;
}
for( int i = 1 ; i <= n ; i ++ ) {
for( int j = 1 ; j <= m ; j ++ ) {
if( ch[ i ][ j ] == '*' ) continue ;
if(i-1>=1&&ch[i-1][j]=='.') ins1((i-1)*m+j,(i-2)*m+j,0), ins2((i-1)*m+j,(i-2)*m+j,0);
if(j-1>=1&&ch[i][j-1]=='.') ins1((i-1)*m+j,(i-1)*m+j-1,1),ins2((i-1)*m+j,(i-1)*m+j-1,0);
if(i+1<=n&&ch[i+1][j]=='.') ins1((i-1)*m+j,i*m+j,0), ins2((i-1)*m+j,i*m+j,0);
if(j+1<=m&&ch[i][j+1]=='.') ins1((i-1)*m+j,(i-1)*m+j+1,0),ins2((i-1)*m+j,(i-1)*m+j+1,1);
}
}
spfa() ;
spfa2() ;
int ans = 0 ;
for( int i = 1 ; i <= n * m ; i ++ ) {
if( d[ i ] <= x && D[ i ] <= y ) ans ++ ;
}
printf( "%d\n" , ans ) ;
}
牛客寒假算法基礎集訓營6 解題報告