codeforce的比赛链接:https://codeforces.com/contest/1260
比赛:https://vjudge.net/contest/381167
补题--5(Round 77 Rated for Div.2)
A (Div.2 F)
题意:
给你一棵树n顶点。的颜色i-这个顶点是hi.树的值定义为∑hi=hj,1≤i<j≤ndis(i,j),在哪里dis(i,j)之间最短路径上的边数。i和j.每个顶点的颜色都丢失了,你只记得hi中的任何整数。li,ri。您想要计算满足这些条件的所有树的值之和。
官方代码:
#include<bits/stdc++.h>
using namespace std;
int n ;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7 ;
vector<int> E[maxn];
vector<int> in[maxn] , out[maxn];
int cm = 0;
int l[maxn] , dfn = 0 , dep[maxn];
int g[maxn];
int siz[maxn] , top[maxn] , h[maxn] , f[maxn];
///-----segment tree
struct seg
{
int l , r;
int sum , add ;
}Node[maxn * 4];
void build(int u,int l,int r)
{
Node[u].l = l , Node[u].r = r;
Node[u].sum = Node[u].add = 0;
if(l == r) return ;
build(u<<1 , l , (l + r >> 1));
build(u<<1|1 , (l + r >>1) + 1 , r);
return ;
}
void pd(int u)
{
Node[u].sum = (1LL*Node[u].add*(Node[u].r - Node[u].l + 1) + Node[u].sum) % mod ;
if(Node[u].l == Node[u].r) {
Node[u].add = 0 ;return ;
}
(Node[u<<1].add += Node[u].add ) %= mod;
(Node[u<<1|1].add += Node[u].add ) %= mod;
Node[u].add = 0 ; return ;
}
void modify(int u,int l,int r,int v)
{
if(Node[u].l == l && Node[u].r == r) {
(Node[u].add += v ) %= mod;
pd(u) ; return ;
return ;
}
pd(u) ;
if(Node[u<<1].r >= r) {modify(u<<1 , l , r , v) ; pd(u<<1|1);}
else if(Node[u<<1|1].l <= l) {modify(u<<1|1 , l , r , v) ; pd(u<<1) ;}
else {
modify(u<<1 , l , Node[u<<1].r , v) ;
modify(u<<1|1 , Node[u<<1|1].l , r , v);
}
Node[u].sum = (Node[u<<1].sum + Node[u<<1|1].sum) % mod;
return ;
}
int query(int u,int l,int r)
{
pd(u) ;
if(Node[u].l == l && Node[u].r == r) return Node[u].sum ;
if(Node[u<<1].r >= r) return query(u<<1 , l , r) ;
else if(Node[u<<1|1].l <= l) return query(u<<1|1 , l , r);
else return (query(u<<1 , l , Node[u<<1].r) + query(u<<1|1 , Node[u<<1|1].l , r)) % mod;
}
///---segment tree end
void dfs(int fa,int u,int d)
{
f[u] = fa;
dep[u] = d;siz[u] = 1;h[u] = -1;
for(int i = 0;i < E[u].size();i++) {
if(E[u][i] != fa) {
dfs(u , E[u][i] , d + 1) ;siz[u] += siz[E[u][i]];
if(h[u] == -1 || siz[E[u][i]] > siz[E[u][h[u]]]) h[u] = i;
}
}
return ;
}
void dfs2(int fa,int u)
{
l[u] = ++dfn;
if(h[u] != -1) {
top[E[u][h[u]]] = top[u] ;
dfs2(u , E[u][h[u]]);
}
for(int i = 0;i < E[u].size();i++) {
if(E[u][i] != fa && i != h[u]) {
top[E[u][i]] = E[u][i] ;
dfs2(u , E[u][i]) ;
}
}
return ;
}
int fpow(int a,int b)
{
int ans = 1;
while(b) {
if(b & 1) ans = (1LL * ans * a) % mod;
a = (1LL * a * a) % mod ;b >>= 1;
}
return ans;
}
void add(int u,int v)
{
while(u) {
modify(1 , l[top[u]] , l[u] , v) ;
u = f[top[u]] ;
}
return ;
}
int cal(int u)
{
int ans = mod - query(1 , 1 , 1);
while(u) {
ans = (ans + query(1 , l[top[u]] , l[u])) % mod;
u = f[top[u]] ;
}
return ans;
}
int main()
{
scanf("%d",&n) ;
int P = 1;
for(int i = 1;i <= n;i++) {
int l , r;scanf("%d%d",&l,&r) ;
in[l].push_back(i) ;
out[r + 1].push_back(i) ; cm = max(cm , r);
g[i] = fpow(r - l + 1 , mod - 2) ;
P = 1LL * P * (r - l + 1) % mod;
}
for(int i = 1;i < n;i++) {
int u , v;scanf("%d%d",&u,&v) ;
E[u].push_back(v) ; E[v].push_back(u) ;
}
dfs(0 , 1 , 0) ; top[1] = 1;
dfs2(0 , 1) ;
build(1 , 1 , n) ;
int ans = 0 , cur = 0;
int d1 = 0 , d2 = 0 , u , d3 = 0;
for(int i = 1;i <= cm;i++) {
for(int j = 0;j < out[i].size();j++) {
u = out[i][j] ;
d1 = (d1 - 1LL * dep[u] * g[u] % mod + mod) % mod;
d2 = (d2 - g[u] + mod) % mod ;
d3 = (d3 - 1LL*dep[u]*g[u] % mod * g[u] % mod + mod) % mod;
add(u , mod - g[u]) ;
cur = (cur - 1LL * g[u] * cal(u) % mod + mod) % mod;
}
for(int j = 0;j < in[i].size();j++) {
u = in[i][j] ;
d1 = (d1 + 1LL * dep[u] * g[u]) % mod;
d2 = (d2 + g[u]) % mod;
d3 = (d3 + 1LL*dep[u]*g[u]%mod*g[u]) % mod;
cur = (cur + 1LL * g[u] * cal(u)) % mod;
add(u , g[u]) ;
}
ans = (ans + 1LL * d1 * d2%mod - 2LL*cur - d3) % mod;
ans = (ans + mod) % mod;
}
ans = 1LL * ans * P % mod;
printf("%d\n",ans);
return 0;
}
B (Div.2 D)
题意:
一共有m个士兵,k个陷阱,时间为t,一个首领,这个首领需要在t时间内尽可能多的将士兵带到boos的面前, 第二行是每个士兵的灵敏度,紧接着是k个陷阱,每个陷阱有l,r,d组成,l代表陷阱的位置,r代表l处的陷阱可以在位置r处被解决,陷阱的灵敏度是d,当陷阱的灵敏度比士兵的灵敏度大时,则可以杀掉士兵。陷阱对首领没有用
问首领最多可以将多少名士兵带到boos的面前。
官方代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
typedef pair<int, int> pt;
#define x first
#define y second
int m, n, k, t;
vector<int> l, r, d, a;
bool can(int x)
{
int mn = int(1e9);
for (int i = 0; i < x; i++)
mn = min(mn, a[i]);
vector<pt> segm;
for (int i = 0; i < k; i++)
if (d[i] > mn)
segm.push_back(make_pair(l[i], r[i]));
int req_time = 0;
sort(segm.begin(), segm.end());
int lastr = 0;
for (auto s : segm)
{
if (s.x <= lastr)
{
req_time += max(0, s.y - lastr);
lastr = max(s.y, lastr);
}
else
{
req_time += s.y - s.x + 1;
lastr = s.y;
}
}
req_time = 2 * req_time + n + 1;
return req_time <= t;
}
int main()
{
#ifdef _DEBUG
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d %d %d %d", &m, &n, &k, &t);
a.resize(m);
for (int i = 0; i < m; i++)
scanf("%d", &a[i]);
sort(a.begin(), a.end());
reverse(a.begin(), a.end());
l.resize(k);
r.resize(k);
d.resize(k);
for (int i = 0; i < k; i++)
scanf("%d %d %d", &l[i], &r[i], &d[i]);
int lf = 0;
int rg = m + 1;
while (rg - lf > 1)
{
int mid = (lf + rg) / 2;
if (can(mid))
lf = mid;
else
rg = mid;
}
printf("%d\n", lf);
return 0;
}
C (Div.2 A)
**题意:**就是有ci个房间要配sumi个散热器,一个成本是个数的两倍,c的总截面数不小于散热器,然后求最小成本。
#include <iostream>
using namespace std;
int main()
{
int t = 0;
int c,sum ;
cin>>t;
while(t--)
{
cin>>c>>sum;
int sum1=(sum/c+1)*(sum/c+1)*(sum%c) ;
int sum2=(sum/c)*(sum/c)*(c-sum%c);
cout<<sum1+sum2<<endl;
}
return 0;
}
D (Div.2 E)
题意:
朋友参加拳击比赛,然而,你的朋友不是最强的共有几名选手参加比赛,然后你贿赂其他的选手是你的朋友获得第一名。
并且你可以分配他们的出场顺序可以求最小的成本贿赂成本。
思路:
我的思路:首先可以是第一种可能,如果朋友是能力最强的时候,那么此刻可以不需要回头成本为零。接下来就是朋友的能力不是最大的时候应该是将追其他的进行分类最小与最大汇入最小的,然后是最大的互相减少,到最后剩下的最小在一和朋友进行对比。这个应该需要用到地皮,但是当时也是不太会使用没有想清楚.
官方思路:
如果我们的朋友是最强壮的拳击手,他就会赢而不受任何贿赂。否则,我们必须贿赂最强壮的拳击手,他可以打败一些拳击手。n2−1其他拳击手(直接或间接)。假设我们选择了他将要击败的拳击手,那么还有另一个最强的拳击手。如果我们的朋友现在是最强的,我们不需要贿赂任何人,否则我们会再次贿赂最强壮的拳击手,他就会被打败。n4−1其他拳击手等等。
唯一不清楚的是哪些拳击手应该被我们贿赂的拳击手打败。我们可以用动态规划来贿赂他们:dpi,j是贿赂的最低成本i让所有拳击手j强者要么被贿赂,要么被贿赂的拳击手打败。的每一个值i我们知道最大数量的拳击手被击败i贿赂拳击手,所以这个动态规划的过渡是:如果我们不能“免费”击败下一个拳击手(我们的被贿赂的拳击手已经尽可能多地击败了对手),我们必须贿赂他;否则,我们要么贿赂他,要么认为他被其他拳击手打败了。
#include <bits/stdc++.h>
using namespace std;
const int LOGN = 20;
const int N = (1 << LOGN) + 99;
const long long INF = 1e18;
int n;
int a[N];
long long dp[LOGN+2][N];
int sum[100];
long long calc(int cnt, int pos){
long long &res = dp[cnt][pos];
if(res != -1) return res;
if(a[pos] == -1) return res = 0;
int rem = sum[cnt] - pos;
res = INF;
if(cnt < LOGN)
res = calc(cnt + 1, pos + 1) + a[pos];
if(rem > 0)
res = min(res, calc(cnt, pos + 1));
return res;
}
int main() {
scanf("%d", &n);
for(int i = 0; i < n; ++i)
scanf("%d", a + i);
for(int i = 1, x = n / 2; i < 100; ++i, x /= 2)
sum[i] = sum[i - 1] + x;
reverse(a, a + n);
memset(dp, -1, sizeof dp);
printf("%lld", calc(0, 0));
return 0;
}
E (Div.2 C)
题意:
就是俩个数r,b然后分别是在这两个数的倍数上涂上red,blue。如果即是r,又是b则自己选取一个颜色,如果都不是则是没有颜色,给定一个数k,是否可以找到以k个连续的颜色。可以obey,否则rebel。
思路:
我的思路,是先找到最大的那个数,然后再最大的数选取一段两个数最多的相同的公约数,然后进行选择再加上剩下的小的那个数。但是我的思路还是有所欠缺,然后找到大佬的思路。
大佬的思路:
假设Max是连续相同颜色篱笆的最大值 即k > Max is OK
那么当我们swap保证b大后 存在:
1.b % r == 0,则Max = (b / r) - 1 ;
2.b % r == 1,那么Max最少最少也要是b / r,此时考虑会不会存在Max+1的情况,我们考虑对于每一个以b为长度的区间中最多能存在几个red,即相连red的最大值,朝着最多安插这个极端去向,在一个length = b的区间中,最左边的red距离左边界距离最短是gcd(r,b)[建议画图,清晰明了]。现在对于这个区间剩下的长度,即b - gcd(b,r)考虑还能放几个red:(b - gcd(b,r)) / r。社上面的答案为cur,这里要特别判断一下是否整除,整除的话最后一个篱笆我们为了使Max最小必然涂成blue,所以cur–。好的到这里只需要看cur ?= Max,最前面那个red是否挤进去了来决定Max是否++就好了。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int inf = 0x3f3f3f3f;
int t,r,b,k,Max;
int gcd(int a,int b){
if(!b) return a;
return gcd(b,a % b);
}
int main()
{
scanf("%d", &t);
while(t--){
scanf("%d %d %d", &r, &b, &k);
if(b < r) swap(r, b);
if(!(b % r)) Max = b / r - 1;//整除关系
else{
Max = b / r;//at least
int fuck = gcd(b,r);
int cur = (b - fuck) / r;
if(!((b - fuck) % r)) cur--;
if(cur == Max) Max++;
}
//Max是最大连续相同篱笆
if(k > Max) printf("OBEY\n");
else printf("REBEL\n");
}
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<memory>
#include<set>
#include<list>
#include<cstring>
#include <unordered_map>
#include<map>
#include<sstream>
#include<iterator>
#include<fstream>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e6 + 1;
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a%b); }
ll quick(ll a, ll b, ll c)//快速幂取模
{
ll ans = 1;
a %= c;
while (b)
{
if (b & 1) ans = ans*a%c;
a = a*a%c;
b >>= 1;
}
return ans%c;
}
ll divi(ll a, ll b, ll p)
{
b = quick(b, p - 2, p); //b的逆元
return a*b%p;
}
int main()
{
ios::sync_with_stdio(false);
int n;
ll a, b,k;
cin >> n;
while (n--)
{
cin >> a >> b >> k;
if (a > b)
swap(a, b);
ll c = gcd(a, b);
if ((b - 1 - c) / a + 1 >= k)
cout << "REBEL" << endl;
else
cout << "OBEY" << endl;
}
return 0;
}
F (Div.2 B)
题意:
就是俩个数,a:=a−x, b:=b−2x或a:=a−2x, b:=b−x,最后a,b都为0;可以yse,否则no。
思路:
首先找到最大的那个数,然后假设最后都归0,那么式子相加最后就可以得到两个要求一个是
(a+b)%3==0)&&a*2>=b,满足则是yes。
#include <iostream>
using namespace std;
int main()
{
int t,a,b;
cin>>t;
while(t--)
{
int m;
cin>>a>>b;
if(a>b)
{
m=a;
a=b;
b=m;
}
if(((a+b)%3==0)&&a*2>=b)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
}