第 3 场 小白入门赛
1、厉不厉害你坤哥(暴力)
2、思维
3、暴力,前缀和,贪心
4、二分
5、DP
6、容斥,双指针
第 3 场 强者挑战赛
2、BFS
5、树上倍增求第k祖先
1. 召唤神坤
题意:
可以发现,如果我们钦定练习生,那么舞力值的。
因此对于而言,需要知道前的最大值跟后的最大值。可以通过数组来存前缀最大跟后缀最大。然后遍历每个即可。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(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. 聪明的交换策略
题意:
思路: 观察题意可以得出,最终的序列为连续的0 + 连续的1或者连续的1 + 连续的0。分别计算出两种情况所需要的交换数即可。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(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. 怪兽突击
题意:
思路:好像跟cf上有道题差不多思路,假设当前将前个怪兽全部击败一次,那么最终消耗的体力应当再加上。从前往后遍历所有的 ,然后取最小值即可。可以在遍历的时候顺便更新了。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(N , 0);
void init(int n){for(int i = 0 ; i <= n ; i ++){a[i] = 0;}
}
void solve()
{cin >> n >> m;vector<int>b(N , 0);vector<int>sum(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. 蓝桥快打
题意:
思路:首先求出小蓝能够进攻多少次,然后再二分攻击力看能否击败小桥。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(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 <<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;
}
5. 奇怪的段
题意:
思路:观察到数据较小,可以考虑DP解决问题。 定义表示前个数,分成段的最大权值和,于是发现第个数必然放在第段中。于是有状态转移方程:。最后输出即可,这道题就愉快的出来了。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(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. 小蓝的反击
题意:
思路:首先只考虑A的倍数的情况,可以发现:若区间范围内的乘积是A的倍数,那么也必然是A的倍数,以此类推....也就是说,若钦定区间左端点 ,可以找到最小的,使得是A的倍数,那么左端点是,满足题意的区间共有个。很容易想到:随着的增大也必然是越来越大的,因此用双指针来代表每个区间左端点 以及所对应的最小的。将所有都遍历完就是最终的满足A的倍数的区间总数。
假设P(A)代表了A的倍数的区间总数。根据容斥原理,,也就是 , 因此分别把两个P求出来相减即可。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>num(N , 0);
vector<LL>prime;//存储素数
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;}}
} set<int>st;
vector<int>st_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. 暖气冰场
题意:
思路:最短路/BFS,把所有格子第一次被暖气覆盖的时间求出来,然后求出所有时间的最大值即可。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(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<pair<int,int>>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. 逃跑
题意:
思路:首先考虑小蓝位于节点之上,且他只会往深度更深的地方走,那么最终小蓝能够坚持的时间为节点所构成的子树的最大深度。假设这个最大深度为,他是可以通过一遍DFS求出来的。然后发现有。因此对于一个起点而言,小蓝需要先尽可能的往上走,因为越往上,其节点的就越大。所以只需要考虑往上最多能走到哪个祖先即可。然后得到即是起点所能坚持的最长时间。
参考倍增法求LCA的过程,求出每个节点的祖先情况即可。
#include <bits/stdc++.h>
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 pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> 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;
vector<int>a(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<int> siz, top, dep, parent, in, out, seq , max_dep;//子树大小 所在重链的顶部节点 深度 父亲 子树DFS序的起点 子树DFS序的终点std::vector<std::vector<int>> 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;
}