A.括號匹配
題意:問一個括號環,在某一點處剪開,是否能成爲一個合法的括號序列。
題解:考慮左右括號數量是否相等即可。
證明:不會。
B. Oooooooo AAAAE
題解:二分圖最小點權覆蓋模板題,建議自行百度學習,一下子也很難講清楚,但真的是模板題。
代碼:
#include <bits/stdc++.h>
#define mem(sx,sy) memset(sx,sy,sizeof(sx))
#define pa pair<int, int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1e5 + 100 ;
const int MAXM = 2e6 + 5 ;
const int INF = 0x3f3f3f3f ;
const int MOD = 1e9 + 7 ;
const double PI = acos ( - 1 ) ;
using namespace std;
struct dinic {
struct edge {
int to, flow, nxt;
} edges[ MAXM] ;
int n, m, cnt, st, en;
int head[ MAXM] , dis[ MAXM] , cur[ MAXM] ;
void init ( int _st, int _en, int _n) {
st = _st, en = _en, n = _n;
cnt = - 1 ;
mem ( head, - 1 ) ;
}
void addedge ( int u, int v, int w) {
edges[ ++ cnt] = { v, w, head[ u] } ;
head[ u] = cnt;
edges[ ++ cnt] = { u, 0 , head[ v] } ;
head[ v] = cnt;
}
int bfs ( ) {
queue< int > Q;
mem ( dis, 0 ) ;
dis[ st] = 1 ;
Q. push ( st) ;
while ( ! Q. empty ( ) ) {
int u = Q. front ( ) ;
Q. pop ( ) ;
if ( u == en) return 1 ;
for ( int i = head[ u] ; i != - 1 ; i = edges[ i] . nxt) {
int v = edges[ i] . to, w = edges[ i] . flow;
if ( ! dis[ v] && w) {
dis[ v] = dis[ u] + 1 ;
Q. push ( v) ;
}
}
}
return dis[ en] != 0 ;
}
int dfs ( int u, int flow) {
int ret = flow, a;
if ( u == en || flow == 0 ) return flow;
for ( int & i = cur[ u] ; i != - 1 ; i = edges[ i] . nxt) {
int v = edges[ i] . to;
if ( dis[ v] == dis[ u] + 1 && ( a = dfs ( v, min ( ret, edges[ i] . flow) ) ) ) {
edges[ i] . flow - = a;
edges[ i ^ 1 ] . flow + = a;
ret - = a;
if ( ! ret) break ;
}
}
if ( ret == flow) dis[ u] = 0 ;
return flow - ret;
}
int maxflow ( ) {
int ans = 0 ;
while ( bfs ( ) ) {
for ( int i = 1 ; i <= n; i++ ) cur[ i] = head[ i] ;
ans + = dfs ( st, INF) ;
}
return ans;
}
} G;
int main ( ) {
int n, m;
scanf ( "%d%d" , & n, & m) ;
int st = 2 * n + 1 ;
int ed = st + 1 ;
G. init ( st, ed, ed) ;
for ( int i = 1 , w; i <= n; i++ ) {
scanf ( "%d" , & w) ;
G. addedge ( n + i, ed, w) ;
}
for ( int i = 1 , w; i <= n; i++ ) {
scanf ( "%d" , & w) ;
G. addedge ( st, i, w) ;
}
while ( m-- ) {
int u, v;
scanf ( "%d%d" , & u, & v) ;
G. addedge ( u, v+ n, INF) ;
}
printf ( "%d\n" , G. maxflow ( ) ) ;
}
C. 給你一個666
題意:給你一個數組,每次給你兩個數字,經過一系列的操作後,問你一個區間內的最大和最小值。
題解:給你a , b a, b a , b ,令c c c 爲a a a 在二進制下的高3位乘b b b 在二進制下的低3位,令d d d 爲b b b 在二進制下的高三位乘a a a 在二進制下的低3位, 即c = ( a / 8 ) ∗ ( b % 8 ) , d = ( a % 8 ) ∗ ( b / 8 ) c=(a/8)*(b\%8),d=(a\%8)*(b/8) c = ( a / 8 ) ∗ ( b % 8 ) , d = ( a % 8 ) ∗ ( b / 8 ) ,區間左端點l = m i n ( a , b ) ∗ m i n ( c , d ) l=min(a,b)*min(c,d) l = m i n ( a , b ) ∗ m i n ( c , d ) ,右端點r = m a x ( a , b ) ∗ m a x ( c , d ) , r=max(a,b)*max(c,d), r = m a x ( a , b ) ∗ m a x ( c , d ) , 然後區間最值詢問可以用ST表或者線段樹。
巨坑:閱讀理解,左端點越界時按1處理,右端點越界時按n處理,也就是假如 l = r = n + 1 l=r=n+1 l = r = n + 1 ,則也要使得l = 1 , r = n l=1,r=n l = 1 , r = n 。
代碼:
#include <bits/stdc++.h>
#pragma warning(disable:4996);
#define mem(sx,sy) memset(sx,sy,sizeof(sx))
#define pa pair<int, int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1e5 + 100 ;
const int MAXM = 2e6 + 5 ;
const int INF = 0x3f3f3f3f ;
const int MOD = 1e9 + 7 ;
const double PI = acos ( - 1 ) ;
using namespace std;
int a[ MAXN] ;
int dpmin[ MAXN] [ 20 ] ;
int dpmax[ MAXN] [ 20 ] ;
void RMQinit ( int n) {
for ( int i = 1 ; i <= n; i++ ) {
dpmin[ i] [ 0 ] = a[ i] ;
dpmax[ i] [ 0 ] = a[ i] ;
}
int m = int ( log ( ( double ) n) / log ( 2.0 ) ) ;
for ( int i = 1 ; i <= m; i++ ) {
for ( int j = 1 ; j + ( 1 << i) - 1 <= n; j++ ) {
dpmin[ j] [ i] = min ( dpmin[ j] [ i - 1 ] , dpmin[ j + ( 1 << ( i - 1 ) ) ] [ i - 1 ] ) ;
dpmax[ j] [ i] = max ( dpmax[ j] [ i - 1 ] , dpmax[ j + ( 1 << ( i - 1 ) ) ] [ i - 1 ] ) ;
}
}
}
int Querymin ( int l, int r) {
int k = int ( log ( double ( r - l + 1 ) ) / log ( 2.0 ) ) ;
return min ( dpmin[ l] [ k] , dpmin[ r - ( 1 << k) + 1 ] [ k] ) ;
}
int Querymax ( int l, int r) {
int k = int ( log ( double ( r - l + 1 ) ) / log ( 2.0 ) ) ;
return max ( dpmax[ l] [ k] , dpmax[ r - ( 1 << k) + 1 ] [ k] ) ;
}
int main ( ) {
int n, k;
scanf ( "%d%d" , & n, & k) ;
for ( int i = 1 ; i <= n; i++ ) {
scanf ( "%d" , & a[ i] ) ;
}
RMQinit ( n) ;
while ( k-- ) {
int a, b;
scanf ( "%d%d" , & a, & b) ;
int ah = a / 8 , al = a % 8 ;
int bh = b / 8 , bl = b % 8 ;
int c = ah * bl, d = al * bh;
int l = min ( c, d) * min ( a, b) ;
int r = max ( c, d) * max ( a, b) ;
l = max ( 1 , l) ;
r = min ( n, r) ;
printf ( "%d %d\n" , Querymax ( l, r) , Querymin ( l, r) ) ;
}
}
D. LiMn2O4的數學之路
題意:該公式爲斐波那契額數列的通項公式,然後問你斐波那契額數列的第n ( n < 1 0 9 ) n(n<10^9) n ( n < 1 0 9 ) 項。
題解:矩陣快速冪裸題,建議自己百度學習。
E. Spinach和發牌姬
題意:大模擬。
題解:按照題意模擬即可,(我就剩這一道還沒過了qaq
F. 猜球球
題意:有n個盒子,有個小球小球出現在每個盒子的概率爲p[i],你現在可以問類似(小球是否在盒子{1,4,……,n}中?)(1-n的一個子集)的問題,問策略最優情況下,猜出小球所在盒子的猜測次數的數學期望。
官方題解:因爲任意一次詢問和回答,都可以確定其中一半的球球集合包含目標球,另一半則不包含目標球。然後再對包含目標球的球球集合進一步劃分,直到包含目標球的集合裏只包含一個球,就可以百分百確定了。這樣就得到了一個決策樹(二叉形狀),二叉決策樹根節點到每個葉子的路到都是期中一種情況的解決方案,顯然深度就是詢問次數。 則有:期望=∑(詢問次數每種情況出現的概率)=∑(葉子對應的深度 它出現在盒子裏的概率)。 而我們知道:這個公式 ∑(深度*元素出現的概率 ) 與某種編碼方案的編碼長度期望公式 相同。詢問次數的期望最小也就是編碼長度的期望最小。而解決這個問題的經典方法就是——哈夫曼樹。(https://blog.csdn.net/GreyBtfly/article/details/89456514)
一句話總結:構造一棵哈夫曼樹,然後有a n s = ∑ i = 1 n p [ i ] ∗ d e e p [ i ] ans = \sum\limits_{i=1}^{n}p[i]*deep[i] a n s = i = 1 ∑ n p [ i ] ∗ d e e p [ i ]
但我這裏有段更加簡短優美的代碼:
#include <bits/stdc++.h>
#pragma warning(disable:4996);
#define mem(sx,sy) memset(sx,sy,sizeof(sx))
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8 ;
const int MAXN = 5000 + 5 ;
const int MAXM = 2e6 + 5 ;
const int MOD = 1e9 + 7 ;
const int INF = 0x3f3f3f3f ;
const double PI = acos ( - 1 ) ;
using namespace std;
#define pa pair<double, int>
vector< int > G[ MAXN] ;
double a[ MAXN] ;
double ans = 0 ;
void dfs ( int cur, int d) {
if ( G[ cur] . size ( ) == 0 ) ans + = a[ cur] * d;
for ( auto it : G[ cur] ) {
dfs ( it, d + 1 ) ;
}
}
int main ( ) {
int n;
scanf ( "%d" , & n) ;
priority_queue< pa, vector< pa> , greater< pa> > Q;
for ( int i = 1 ; i <= n; i++ ) {
scanf ( "%lf" , & a[ i] ) ;
if ( a[ i] == 0 ) {
n-- ; i-- ; continue ;
}
Q. push ( pa ( a[ i] , i) ) ;
}
int cnt = n;
while ( Q. size ( ) >= 2 ) {
pa t1 = Q. top ( ) ; Q. pop ( ) ;
pa t2 = Q. top ( ) ; Q. pop ( ) ;
pa np ( t1. first + t2. first, ++ cnt) ;
Q. push ( np) ;
G[ cnt] . push_back ( t1. second) ;
G[ cnt] . push_back ( t2. second) ;
}
int root = Q. top ( ) . second;
dfs ( root, 0 ) ;
printf ( "%.7lf\n" , ans) ;
}
G. 似魔鬼的步伐
題意:你有n n n 塊錢,初始能力a n s = 0 ans=0 a n s = 0 ,你可以花一塊錢使得a n s = a n s + 1 ans=ans+1 a n s = a n s + 1 ,或者花兩塊錢使得a n s = 2 ∗ a n s + 2 ans=2*ans+2 a n s = 2 ∗ a n s + 2 ,問a n s ans a n s 最大值。
題解:貪心,花一個兩塊 比 花兩個一塊要賺,n爲偶數的話一直花兩塊即可,n爲奇數的話先花一個一塊,然後一直花兩塊,數據範圍較小,矩陣快速冪都不用。
H. 挑剔程度
題意:n個值,你可以最多去掉k個值,問剩下的最大值。
題解:溫暖的簽到題,排個序就可以了,注意k是可以大於n的。
I. 熱狗樹
題意:有一棵點權爲0或1的樹,問所有0的點到所有1的距離和以及所有1的點到所有0的距離和。
題解:樹形dp,設n u m [ 0 ] num[0] n u m [ 0 ] 和n u m [ 1 ] num[1] n u m [ 1 ] 分別爲點權爲0或者1的點的個數,由dp可以得到u u u 號節點的s i z [ 0 ] [ u ] siz[0][u] s i z [ 0 ] [ u ] 和s i z [ 1 ] [ u ] siz[1][u] s i z [ 1 ] [ u ] 分別爲u u u 節點子樹點權爲0和子樹點權爲1的點的個數,則對於某一個點u,其連着他爸爸的那條邊的邊權爲w,則這條邊對於答案的貢獻爲
a n s + = w ∗ ( s i z [ 0 ] ∗ ( n u m [ 1 ] − s i z [ 1 ] ) + s i z [ 1 ] ∗ ( n u m [ 0 ] − s i z [ 0 ] ) ) ans+=w*(siz[0]*(num[1]-siz[1])+siz[1]*(num[0]-siz[0])) a n s + = w ∗ ( s i z [ 0 ] ∗ ( n u m [ 1 ] − s i z [ 1 ] ) + s i z [ 1 ] ∗ ( n u m [ 0 ] − s i z [ 0 ] ) )
至於爲什麼你們自己稍微想一想應該就能理解了!(考慮一條邊將一棵樹分成了兩個部分)記得取模。
代碼:
#include <bits/stdc++.h>
#pragma warning(disable:4996);
#define mem(sx,sy) memset(sx,sy,sizeof(sx))
#define pa pair<int, int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 2e5 + 5 ;
const int MAXM = 2e6 + 5 ;
const int INF = 0x3f3f3f3f ;
const double PI = acos ( - 1 ) ;
const int MOD = 998244353 ;
using namespace std;
vector< pa> G[ MAXN] ;
int a[ MAXN] ;
int siz01[ MAXN] ;
int siz[ 2 ] [ MAXN] ;
ll ans = 0 ;
int n, sz[ 2 ] = { 0 , 0 } ;
void dfs1 ( int cur, int fa) {
siz[ a[ cur] ] [ cur] = 1 ;
for ( auto it : G[ cur] ) {
int v = it. first;
if ( v == fa) continue ;
dfs1 ( v, cur) ;
siz[ 0 ] [ cur] + = siz[ 0 ] [ v] ;
siz[ 1 ] [ cur] + = siz[ 1 ] [ v] ;
}
}
void dfs2 ( int cur, int fa) {
for ( auto it : G[ cur] ) {
int v = it. first;
int w = it. second;
if ( v == fa) continue ;
ans + = 1ll * siz[ 1 ] [ v] * ( sz[ 0 ] - siz[ 0 ] [ v] ) % MOD * w;
ans % = MOD;
ans + = 1ll * siz[ 0 ] [ v] * ( sz[ 1 ] - siz[ 1 ] [ v] ) % MOD * w;
ans % = MOD;
dfs2 ( v, cur) ;
}
}
int main ( ) {
scanf ( "%d" , & n) ;
for ( int i = 1 ; i <= n; i++ ) {
scanf ( "%d" , & a[ i] ) ;
sz[ a[ i] ] ++ ;
}
for ( int i = 1 , u, v, w; i < n; i++ ) {
scanf ( "%d%d%d" , & u, & v, & w) ;
G[ u] . emplace_back ( v, w) ;
G[ v] . emplace_back ( u, w) ;
}
dfs1 ( 1 , 0 ) ;
dfs2 ( 1 , 0 ) ;
printf ( "%lld\n" , ans * 2 % MOD) ;
}
J. 流浪西郵之尋找火石碎片
題意:n件物品,每件有兩個不同的貨幣的價格c1,c2,你可以使用任意一種貨幣購買它,和其價值v,你現在有兩種貨幣a,b(對應c1,c2)你現在還能免費拿k件物品,問最大價值是多少。
考慮二維01揹包,再加一維霸王餐,令d p [ i ] [ x ] [ y ] [ j ] dp[i][x][y][j] d p [ i ] [ x ] [ y ] [ j ] 爲第i件物品,花費x單位的第一種貨幣,y單位的第二種貨幣,用了j次霸王餐的最大值,則有暴力轉移:
d p [ i ] [ x ] [ y ] [ j ] = m a x ( d p [ i − 1 ] [ x ] [ y ] [ j ] , d p [ i − 1 ] [ x − c 1 [ i ] ] [ y ] [ j ] + v [ i ] , d p [ i − 1 ] [ x ] [ y − c 2 [ i ] ] [ j ] + v [ i ] , d p [ i − 1 ] [ x ] [ y ] [ j − 1 ] + v [ i ] ) dp[i][x][y][j]=max(dp[i-1][x][y][j], \quad dp[i-1][x-c1[i]][y][j]+v[i], \quad dp[i-1][x][y-c2[i]][j]+v[i], \quad dp[i-1][x][y][j-1]+v[i] ) d p [ i ] [ x ] [ y ] [ j ] = m a x ( d p [ i − 1 ] [ x ] [ y ] [ j ] , d p [ i − 1 ] [ x − c 1 [ i ] ] [ y ] [ j ] + v [ i ] , d p [ i − 1 ] [ x ] [ y − c 2 [ i ] ] [ j ] + v [ i ] , d p [ i − 1 ] [ x ] [ y ] [ j − 1 ] + v [ i ] )
轉移考慮清楚後就像01揹包那樣寫就行了,數據範圍不大,空間夠用,不用優化,注意史詩級wa點:多組輸入。
代碼:
#include <bits/stdc++.h>
#pragma warning(disable:4996);
#define mem(sx,sy) memset(sx,sy,sizeof(sx))
#define pa pair<int, int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1e5 + 100 ;
const int MAXM = 2e6 + 5 ;
const int INF = 0x3f3f3f3f ;
const int MOD = 1e9 + 7 ;
const double PI = acos ( - 1 ) ;
using namespace std;
int dp[ 105 ] [ 105 ] [ 105 ] [ 10 ] ;
int c1[ 105 ] , c2[ 105 ] , v[ 105 ] ;
int main ( ) {
int n, m1, m2, k;
while ( ~ scanf ( "%d%d%d%d" , & n, & m1, & m2, & k) ) {
for ( int i = 1 ; i <= n; i++ ) {
scanf ( "%d%d%d" , & c1[ i] , & c2[ i] , & v[ i] ) ;
}
int ans = 0 ;
mem ( dp, 0 ) ;
for ( int i = 1 ; i <= n; i++ ) {
for ( int x = 0 ; x <= m1; x++ ) {
for ( int y = 0 ; y <= m2; y++ ) {
for ( int j = 0 ; j <= k; j++ ) {
dp[ i] [ x] [ y] [ j] = dp[ i - 1 ] [ x] [ y] [ j] ;
if ( x >= c1[ i] ) {
dp[ i] [ x] [ y] [ j] = max ( dp[ i] [ x] [ y] [ j] , dp[ i - 1 ] [ x - c1[ i] ] [ y] [ j] + v[ i] ) ;
}
if ( y >= c2[ i] ) {
dp[ i] [ x] [ y] [ j] = max ( dp[ i] [ x] [ y] [ j] , dp[ i - 1 ] [ x] [ y - c2[ i] ] [ j] + v[ i] ) ;
}
if ( j > 0 ) {
dp[ i] [ x] [ y] [ j] = max ( dp[ i] [ x] [ y] [ j] , dp[ i - 1 ] [ x] [ y] [ j - 1 ] + v[ i] ) ;
}
ans = max ( dp[ i] [ x] [ y] [ j] , ans) ;
}
}
}
}
printf ( "%d\n" , ans) ;
}
}
K. 到底有多少個小和尚?
題意:給你a,b,計算a n s = ∑ i = a b i ∗ ( i + 1 ) ans=\sum\limits_{i=a}^{b}i*(i+1) a n s = i = a ∑ b i ∗ ( i + 1 ) 。
題解:溫暖的簽到題,前綴和搞搞就行了。
。
。
總結,比賽較爲溫暖,幾乎沒有什麼難題,但坑點很多導致比賽體驗極差,比如一半題不要多組輸入,另一半題要多組輸入,比如1e5的cin能T成傻逼,比如語文閱讀理解,再比如B的樣例出鍋……讓我前後在這種地方wa了30幾發,網絡賽還好,要是現場賽估計已經被隊友打死了。