第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)

第 3 场 小白入门赛

1、厉不厉害你坤哥(暴力)

2、思维

3、暴力,前缀和,贪心

4、二分

5、DP

6、容斥,双指针

第 3 场 强者挑战赛

2、BFS

5、树上倍增求第k祖先

1. 召唤神坤

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第1张图片

可以发现,如果我们钦定练习生j,那么舞力值的(max(W_{1} , W_{2}...W_{j - 1}) +max(W_{j + 1} , W_{j + 2} ... W_{n})) / W_{j}

因此对于j而言,需要知道前j - 1的最大值跟后j +1的最大值。可以通过数组来存前缀最大跟后缀最大。然后遍历每个j即可。

        

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
    return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
    return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
    for(int i = 0 ; i <= n ; i ++){
        a[i] = 0;
    }
}
void solve() 
{
    cin >> n;
    int l[n + 5] ,r[n + 5];
    memset(l , 0 , sizeof l);
    memset(r , 0 , sizeof r);
    for(int i = 1 ; i <= n ; i ++){
        cin >> a[i];
    }
    for(int i = 1 ; i <= n ; i++){
        l[i] = max(l[i - 1] , a[i]);
    }
    for(int i = n ; i >= 1 ; i --){
        r[i] = max(r[i + 1] , a[i]);
    }
    int ans = 0;
    for(int i = 2 ; i < n ; i ++){
        ans = max(ans , (l[i - 1] + r[i + 1]) / a[i]);
    }
    cout << ans;
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
//    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

2. 聪明的交换策略 

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第2张图片

思路: 观察题意可以得出,最终的序列为连续的0 + 连续的1或者连续的1 + 连续的0。分别计算出两种情况所需要的交换数即可。

        

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	cin >> n;
	string s;
	cin >> s;
	for(int i = 0 ; i < n ; i ++){
		a[i] = s[i] - '0';
	}
	int id = 0;
	int ans = 0;
	for(int i = 0 ; i < n ; i ++){
		if(a[i] == 1){
			ans += i - id;
			id++;
		}
	}
	id = n - 1;
	int ans1 = 0;
	for(int i = n - 1 ; i >= 0 ; i--){
		if(a[i] == 1){
			ans1 += id - i;
			id--;
		}
	}
	cout << min(ans , ans1);
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
//	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

3. 怪兽突击 

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第3张图片

        思路:好像跟cf上有道题差不多思路,假设当前将前i个怪兽全部击败一次,那么最终消耗的体力应当再加上min(a_{1} + b_{1} , a_{2} + b_{2} ..., a_{i} + b_{i}) * (k - i)。从前往后遍历所有的 i,然后取最小值即可。min(a_{1} + b_{1} , a_{2} + b_{2} ..., a_{i} + b_{i})可以在遍历的时候顺便更新了。

        

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
#define int long long
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	cin >> n >> m;
	vectorb(N , 0);
	vectorsum(N, 0);
	for(int i = 1 ; i <= n ; i ++)	{
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
	}
	for(int i = 1 ; i <= n ; i ++){
		cin >> b[i];
	}
	int mi = 1e9;
	int ans = 1e18;
	for(int i = 1 ; i <= n ; i ++){
		mi = min(mi , a[i] + b[i]);
		int tmp = sum[i] + max(m - i , 0 * 1LL) * mi;
		ans = min(tmp , ans);
	}
	cout << ans;
}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
//	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

4. 蓝桥快打 

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第4张图片

        思路:首先求出小蓝能够进攻多少次,然后再二分攻击力看能否击败小桥。

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
#define int long long
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	int a , b , c;
	cin >> a >> b >> c;
	int t = (a - 1) / c + 1;
	auto check = [&](int x){
		if(x * t >= b){
			return true;
		}
		else{
			return false;
		}
	};
	int l = 1 , r = 1e9;
	while(l < r){
		int mid = (l + r) / 2;
		if(check(mid)){
			r = mid;
		}
		else{
			l = mid + 1;
		}
	}
	cout << l <>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

5. 奇怪的段 

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第5张图片

        思路:观察到数据较小,可以考虑DP解决问题。 定义dp[i][j]表示前i个数,分成j段的最大权值和,于是发现第i个数必然放在第j段中。于是有状态转移方程:dp[i][j] = max(dp[i - 1][j] , dp[i - 1][j - 1]) + a[i] * p[j]。最后输出dp[n][k]即可,这道题就愉快的出来了。

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
#define int long long
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	cin >> n >> m;
	int dp[n + 5][m + 5];
	int p[m + 5];
	memset(dp , -0x3f , sizeof dp);
	dp[0][0] = 0;
	for(int i = 1 ; i <= n ; i ++){
		cin >> a[i];
	}	
	for(int i = 1 ;i <= m ; i ++){
		cin >> p[i];
	}
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= m ; j ++){
			dp[i][j] = max(dp[i - 1][j] , dp[i - 1][j - 1]) + a[i] * p[j];
		}
	}
	cout << dp[n][m];
}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
    while(t--)
    {
    	solve();
    }
    return 0;
}

6. 小蓝的反击 

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第6张图片

        思路:首先只考虑A的倍数的情况,可以发现:若区间[i , j]范围内的乘积是A的倍数,那么[i , j + 1]也必然是A的倍数,以此类推....也就是说,若钦定区间左端点 i ,可以找到最小的j,使得\prod _{x = i}^{j} a_{x}是A的倍数,那么左端点是i,满足题意的区间共有n - j + 1个。很容易想到:j随着i的增大也必然是越来越大的,因此用双指针来代表每个区间左端点 i 以及所对应的最小的j。将所有i都遍历完就是最终的满足A的倍数的区间总数。

       假设P(A)代表了A的倍数的区间总数。根据容斥原理,P(A \cap !B) = P(A) - P(A\cap B),也就是P(A \cap !B) = P(A) - P(lcm(A , B)) , 因此分别把两个P求出来相减即可。

        

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
#define int long long
const LL maxn = 4e05+7;
const LL N = 1e06+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectornum(N , 0);
vectorprime;//存储素数
bool vis[N+5];
void su() 
{
	for(int i=2;i<=N;i++)
	{
		if(!vis[i])
		prime.pb(i);
		for(int j=0;j < (int)prime.size() && prime[j] * i <= N;j ++)
		{
			vis[prime[j]*i]=1;
			if(i % prime[j]==0)
			break;
		}
	}
} 

setst;
vectorst_num;
int len;
int ans(int x){
	int maxx[len];
	int cnt[len];
	memset(cnt , 0 , sizeof cnt);
	memset(maxx , 0 , sizeof maxx);
	for(int i = 0 ; i < len ; i++){
		while(x % st_num[i] == 0){
			maxx[i]++;
			x /= st_num[i];
		}
	}
	int out = 0;
	int f = 1;
	for(int i = 0 , j = 0 ; i < n ; i ++){
		if(j <= i){
			memset(cnt , 0 , sizeof cnt);
			j = i;
			int tmp = num[j];
			for(int t = 0 ; t < len ; t ++){
				while(tmp % st_num[t] == 0){
					cnt[t]++;
					tmp /= st_num[t];
				}
			}			
			j++;
			f = 1;
		}
		for(int t = 0 ; t < len ; t ++){
			if(cnt[t] < maxx[t]){
				f = 0;
			}
		}
		while(j < n && !f){
			int tmp = num[j];
			for(int t = 0 ; t < len ; t ++){
				while(tmp % st_num[t] == 0){
					cnt[t]++;
					tmp /= st_num[t];
				}
			}
			f = 1;		
			for(int t = 0 ; t < len ; t ++){
				if(cnt[t] < maxx[t]){
					f = 0;
					break;
				}
			}
			j++;	
		}
		if(f){
			out += (n - j + 1);
		}
		int tmp = num[i];
		for(int t = 0 ; t < len ; t ++){
			while(tmp % st_num[t] == 0){
				cnt[t]--;
				tmp /= st_num[t];
			}
		}		
	}
	return out;
}
void solve() 
{
	int  a , b;
	cin >> n >> a >> b;
	int A = a , B = b;
	for(int i = 0 ; i < n ; i ++){
		cin >> num[i];
	}
	int t = prime.size();
	for(int i = 0 ; i < t ; i ++){
		while(a % prime[i] == 0){
			st.insert(prime[i]);
			a /= prime[i];
		}
	}	
	if(a > 1){
		st.insert(a);
	}
	for(int i = 0 ; i < t ; i ++){
		while(b % prime[i] == 0){
			st.insert(prime[i]);
			b /= prime[i];
		}
	}	
	if(b > 1){
		st.insert(b);
	}
	for(auto it : st){
		st_num.pb(it);
	}
	len = st_num.size();
	cout << ans(A) - ans(A * B / gcd(A , B))<< endl;

}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
	su();
    int t=1;
	//cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

 2. 暖气冰场

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第7张图片

        思路:最短路/BFS,把所有格子第一次被暖气覆盖的时间求出来,然后求出所有时间的最大值即可。

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
#define int long long
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
int vis[1010][1010];
int tx[8] = {-1 , 1 , -1 , 1 , 1 , -1 , 0 , 0};
int ty[8] = {-1 , 1 , 1 , -1 , 0 , 0 , 1 , -1};
void solve() 
{
	cin >> n >> m;
	int t;
	cin >> t;
	int mp[n + 5][m + 5];
	memset(mp , 0x3f , sizeof mp);
	queue>q;
	for(int i = 0 ; i < t ; i ++){
		int x , y;
		cin >> x >> y;
		mp[x][y] = 0;
		vis[x][y] = 1;
		q.push({x , y});
	}
	auto check = [&](int x , int y){
		return x >=1 && x <= n && y >= 1 && y <= m && vis[x][y] == 0;
	};
	int ans = 0;
	while(!q.empty()){
		auto it = q.front();
		q.pop();
		int x = it.first;
		int y = it.second;
		for(int i = 0 ; i < 8 ; i ++){
			int nx = x + tx[i];
			int ny = y + ty[i];
			if(check(nx , ny)){
				mp[nx][ny] = mp[x][y] + 1;
				vis[nx][ny] = 1; 
				q.push({nx , ny});
			}
		}
	}
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ;j <= m ; j++){
			ans = max(ans , mp[i][j]);
		}
	}
	cout << ans;
}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
    while(t--)
    {
    	solve();
    }
    return 0;
}

 5. 逃跑

        题意:第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)_第8张图片

        思路:首先考虑小蓝位于节点x之上,且他只会往深度更深的地方走,那么最终小蓝能够坚持的时间为节点x所构成的子树的最大深度。假设这个最大深度为max\_dep[x],他是可以通过一遍DFS求出来的。然后发现有max\_dep[x] \leq max\_dep[parent[x]]。因此对于一个起点x而言,小蓝需要先尽可能的往上走,因为越往上,其节点的max\_dep就越大。所以只需要考虑往上最多能走到哪个祖先y即可。然后得到max\_dep[y]即是起点x所能坚持的最长时间。

        参考倍增法求LCA的过程,求出每个节点的祖先情况即可。

        

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
#define int long long
const LL maxn = 4e05+7;
const LL N = 1e06+10;
const LL mod = 998244353;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vectora(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
int dp[N][30];
struct HLD {//轻重链剖分
    int n;
    std::vector siz, top, dep, parent, in, out, seq , max_dep;//子树大小 所在重链的顶部节点 深度 父亲 子树DFS序的起点 子树DFS序的终点
    std::vector> adj;
    int cur = 1;
    HLD() {}
    HLD(int n) {
        init(n);
    }
    void init(int n) {
        this->n = n;
        siz.resize(n);
        top.resize(n);
        dep.resize(n);
        parent.resize(n);
        in.resize(n);
        out.resize(n);
        seq.resize(n);
        max_dep.resize(n);
        cur = 0;
        adj.assign(n, {});
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void work(int root = 1) {
        top[root] = root;
        dep[root] = 0;
        parent[root] = -1;
        dfs1(root);
        dfs2(root);
    }
    void dfs1(int u) {
        if (parent[u] != -1) {
            adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u]));
        }
        siz[u] = 1;
        for (auto &v : adj[u]) {
            parent[v] = u;
            dep[v] = dep[u] + 1;
            dfs1(v);
            siz[u] += siz[v];
            if (siz[v] > siz[adj[u][0]]) {
                std::swap(v, adj[u][0]);
            }
        }
    }
    void dfs2(int u) {
        in[u] = ++cur;
        max_dep[u] = dep[u];
        seq[in[u]] = u;
        for (auto v : adj[u]) {
            top[v] = v == adj[u][0] ? top[u] : v;
            dfs2(v);
            max_dep[u] = max(max_dep[u] , max_dep[v]);
        }
        out[u] = cur;
    }
    int lca(int u, int v) {
        while (top[u] != top[v]) {
            if (dep[top[u]] > dep[top[v]]) {
                u = parent[top[u]];
            } else {
                v = parent[top[v]];
            }
        }
        return dep[u] < dep[v] ? u : v;
    }
    void dfs3(){
    	for(int i = 1 ; i <= n - 5 ; i ++){
    		dp[i][0] = parent[i];
    	}
    	for(int i = 1 ; i <= n - 5; i ++){
	    	for(int j = 1 ; j < 30 ; j ++){
	    		if(dp[i][j - 1] < 1){
	    			continue;
	    		}
	    		dp[i][j] = dp[dp[i][j - 1]][j - 1];
	    	}
	    }
    }
    int find_an(int x , int y){
    	for(int i = 20 ; i >= 0 ; i--){
			int t = 1 << i;
			if(y >= t){
				x = dp[x][i];
				y -= t;
			}
    	}
    	return x;
    }
    int dist(int u, int v) {
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }
    
    int jump(int u, int k) {
        if (dep[u] < k) {
            return -1;
        }
        
        int d = dep[u] - k;
        
        while (dep[top[u]] > d) {
            u = parent[top[u]];
        }
        
        return seq[in[u] - dep[u] + d];
    }
    
    bool isAncester(int u, int v) {//是否为祖先
        return in[u] <= in[v] && in[v] < out[u];
    }
    
    int rootedParent(int u, int v) {
        std::swap(u , v);
        if (u == v) {
            return u;
        }
        if (!isAncester(u, v)) {
            return parent[u];
        }
        auto it = std::upper_bound(adj[u].begin(), adj[u].end(), v, [&](int x, int y) {
            return in[x] < in[y];
        }) - 1;
        return *it;
    }
    int rootedSize(int u, int v) {
        if (u == v) {
            return n;
        }
        if (!isAncester(v, u)) {
            return siz[v];
        }
        return n - siz[rootedParent(u, v)];
    }
    
    int rootedLca(int a, int b, int c) {
        return lca(a, b) ^ lca(b, c) ^ lca(c, a);
    }
}hld;
LL qpow(LL a , LL b)//快速幂
{
	LL sum=1;
	while(b){
		if(b&1){
			sum=sum*a%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return sum;
}
void solve() 
{
	cin >> n;
	hld.init(n + 5);
	memset(dp , 0 ,sizeof dp);
	for(int i = 1 ; i < n ; i ++){
		int u , v;
		cin >> u >> v;
		hld.addEdge(u , v);
	}
	hld.work();
	hld.dfs3();
	int ans = 0;
	for(int i = 2 ; i <= n ; i ++){
		int t = hld.dep[i];
		if(t <= 2){
			ans += hld.max_dep[i];
		}
		else{
			int to = (t - 1) / 2;
			int an = hld.find_an(i , to);
			ans += hld.max_dep[an];
		}
	}
  ans %= mod;
	cout << (ans * qpow(n , mod - 2)) % mod<< endl;
}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	//cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

你可能感兴趣的:(算法,数据结构,c++,蓝桥杯)