A. Orders
按天数ai排序,检查存货量即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
#define ll long long
struct QWQ
{
ll x,y;
}a[N];
bool cmp(QWQ A,QWQ B)
{
return A.x < B.x;
}
void Solve()
{
ll n,m;
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
{
cin >> a[i].x >> a[i].y;
}
sort(a + 1, a + n + 1,cmp);
ll sum = 0;
for(int i = 1; i <= n; i ++ )
{
sum += a[i].y;
if(sum > a[i].x * m)
{
cout << "No" << '\n';
return;
}
}
cout << "Yes" << '\n';
}
int main()
{
int t = 1;
cin >> t;
for(int i = 1; i <= t; i ++ ) Solve();
return 0;
}
B. Building Company
这道题目有点像拓扑,我们先将不同工种所对应的项目和要求人数分别存起来,再将每个项目可以吸引的不同工种和人数也存起来。
我们从已有的工种去判断满足哪些工程对这个工种的需求,如果满足则对这个工程进行+1的标记,直到这个工程所有的工种限制全部满足时,再将该项目吸引的工种进行更新,并重新判断该工种所对应的工程。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
#define ll long long
#define edl '\n'
map<ll,set<pair<ll,ll>>>mp;
map<ll,ll>now;
ll goal[N],res[N];
vector<pair<ll,ll>> add[N];
void Solve()
{
ll n,m;
cin >> n;
for(int i = 1; i <= n; i ++ )
{
ll x,y;
cin >> x >> y;
now[x] += y;
}
cin >> m;
ll ans = 0;
ll s;
for(int i = 1; i <= m; i ++ )
{
cin >> s;
goal[i] = s;
for(int j = 1; j <= s; j ++ )
{
ll x,y;
cin >> x >> y;
mp[x].insert({y,i});
}
cin >> s;
if(!goal[i]) ans ++;
for(int j = 1; j <= s; j ++ )
{
ll x,y;
cin >> x >> y;
if(!goal[i]) now[x] += y;
else add[i].push_back({x,y});
}
}
vector<ll>check;
for(auto &it : mp)
{
vector<pair<ll,ll>>re;
for(auto [x,y] : it.second)
{
if(x <= now[it.first])
{
re.push_back({x,y});
res[y] ++;
if(res[y] == goal[y])
{
ans ++;
for(auto [xx,yy] : add[y])
{
if(xx < it.first) check.push_back(xx);
now[xx] += yy;
}
}
}else break;
}
for(auto itt : re)
{
it.second.erase(itt);
}
}
while(check.size())
{
vector<ll>tmp;
for(auto it : check)
{
vector<pair<ll,ll>>re;
for(auto [x,y] : mp[it])
{
if(x<=now[it])
{
re.push_back({x,y});
res[y] ++;
if(res[y]==goal[y])
{
ans++;
for(auto [xx,yy]:add[y])
{
tmp.push_back(xx);
now[xx]+=yy;
}
}
}else break;
}
for(auto itt:re){
mp[it].erase(itt);
}
}
check = tmp;
}
cout << ans << edl;
}
int main()
{
int t = 1;
// cin >> t;
for(int i = 1; i <= t; i ++ ) Solve();
return 0;
}
D. Fast and Fat
这道题,首先你能快速想到二分答案然后判断是否成立。
假设答案为x 那么,之有v1 + w1 - w2 >= x 时1才可以帮助2
可以将式子转化为v1 + w1 - x >= w2
那么怎么判断呢,赛时的时候我用了最暴力的lowerbound,去查找第一个大于等于需要帮助的人,这样是最贪心的。复杂度是nloglog
赛后突然发现,其实完全不用这么麻烦,只需要先按v1 + w1排序,再按w1排序能力最强的帮助困难最大的即可,复杂度nlog
struct QWQ
{
ll v,w;
}a[N],b[N];
bool cmp(QWQ A,QWQ B)
{
return A.v + A.w < B.v + B.w;
}
bool cmp1(QWQ A,QWQ B)
{
return A.w < B.w;
}
int ck(ll x)
{
vector<ll>g(n,0);
ll l = 0;
ll r = 0;
for(int i = n; i >= 1; i -- )
{
if(a[i].v >= x)
{
g[l++] = (a[i].v + a[i].w - x);
}
}
for(int i = n; i >= 1; i -- )
{
if(b[i].v < x)
{
if(g[r++] < b[i].w) return 0;
}
}
return 1;
}
void Solve()
{
cin >> n;
for(int i = 1; i <= n; i ++ )
{
cin >> a[i].v >> a[i].w;
b[i] = a[i];
}
sort(a + 1, a + n + 1,cmp);
sort(b + 1, b + n + 1,cmp1);
ll l = 0;
ll r = 10000000000;
while(l <= r)
{
ll mid = (l + r) >> 1;
if(ck(mid)) l = mid + 1;
else r = mid - 1;
}
cout << r << edl;
}
E. Math Problem
这题一定是先除法n次再乘法m次,绝不可能乘完再除这样是无意义的。
所以只需要枚举除法次数再枚举乘法次数即可。
ll get(ll n, ll m, ll a)
{
ll inv = 1;
ll now = 0;
for(ll x = n % m; 1; x = (x * k) % m, inv *= k)
{
if(x + inv > m || x == 0)
{
return now;
}
now += a;
}
return -1;
}
void Solve()
{
cin >> n >> k >> m >> a >> b;
if(n % m == 0)
{
cout << "0" << edl;
return;
}
if(k == 1)
{
cout << "-1" << edl;
return;
}
ll ans = 3e18;
ll now = 0;
for(ll x = n; x % m != 0; x /= k)
{
ans = min(ans, get(x, m, a) + now);
now += b;
}
ans = min(ans, now);
cout << ans << edl;
}
G. Matching
i - j == ai - aj转换为ai - i == aj - j
所以我们可以将其分成不同的集合,集合内贪心即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
#define ll long long
#define edl '\n'
ll a[N];
map<ll,set<pair<ll,ll>>>mp;
void Solve()
{
mp.clear();
ll n;
cin >> n;
for(int i = 1; i <= n; i ++ )
{
cin >> a[i];
mp[a[i] - i].insert({a[i],i});
}
ll ans = 0;
for(auto it : mp)
{
auto g = it.second;
pair<ll,ll>A;
for(auto ok : g)
{
A = ok;
break;
}
if(g.size() % 2) g.erase(A);
ll sum = 0;
ll ok1 = 0;
for(auto ok : g)
{
if(ok1)
{
ok1 = 0;
sum += ok.first;
if(sum >= 0) ans += sum;
sum = 0;
}
else
{
ok1 = 1;
sum += ok.first;
}
}
}
cout << ans << edl;
}
int main()
{
int t = 1;
cin >> t;
for(int i = 1; i <= t; i ++ ) Solve();
return 0;
}
I Three Dice
枚举所有点数即可ac
J. Not Another Path Query Problem
这是一道位运算的题目,我们很容易想到按位去枚举答案。
一条路径的边权>=V可以分成两种情况:
1.等于也就是V二进制中为1的,路径边权值上也一定要为1
2.大于,V的第i位为0时,边权这一位为1,前面与V相同,后面任意
然后我们可以通过并查集来判断了
int ans[N];
struct node
{
int u,v;
ll w;
}e[N];
int fa[N];
int getf(int x)
{
if(fa[x] == x) return x;
return fa[x] = getf(fa[x]);
}
void mergee(int u,int v)
{
u = getf(u); v = getf(v);
if(u != v) fa[u] = v;
}
int qu[N],qv[N];
void Solve(){
int n,m,Q;
ll V;
cin >> n >> m >> Q >> V;
for(int i = 1;i <= m;i ++) cin >> e[i].u >> e[i].v >> e[i].w;
for(int i = 1;i <= Q;i ++) cin >> qu[i] >> qv[i];
ll pre = 0;
for(int i = 60;i >= -1;i --)
{
if(i >= 0 && ((V >> i) & 1)) pre |= (1ll << i);
else
{
ll cur = pre | (1ll << i);
if(i == -1) cur = V;
for(int j = 1;j <= n;j ++) fa[j] = j;
for(int j = 1;j <= m;j ++)
{
if((cur & e[j].w) == cur) mergee(e[j].u,e[j].v);
}
for(int j = 1;j <= Q;j ++) if(getf(qu[j]) == getf(qv[j])) ans[j] = 1;
}
}
for(int i = 1;i <= Q;i ++) cout << (ans[i] ? "Yes" : "No") << edl;
}
L. Puzzle: Sashigane
你先想一想一个边长为n的正方形是不是一定能同过m个L变成边长为n+m的正方形。
这道题就考了这个,剩下的交给dfs。。。
struct R
{
ll a, b, c, d;
};
vector<R>g;
void dfs (int x, int y, int x2, int y2)
{
if (x == x2 && y == y2) return;
if (x != u && y != v)
{
g.push_back({x, y, x2 - x, x2 - x});
dfs (x + 1, y + 1, x2, y2);
}
else if (x != u && y2 != v)
{
g.push_back({x, y2, x2 - x, x - x2});
dfs (x + 1, y, x2, y2 - 1);
}
else if (x2 != u && y != v)
{
g.push_back({x2, y, x - x2, x2 - x});
dfs (x, y + 1, x2 - 1, y2);
}
else if (x2 != u && y2 != v)
{
g.push_back({x2, y2, x - x2, x - x2});
dfs (x, y, x2 - 1, y2 - 1);
}
}
void solve()
{
cin >> n >> u >> v;
dfs (1, 1, n, n);
cout << "Yes" << edl;
cout << g.size() << edl;
for (auto [a, b, c, d] : g)
{
cout << a << " " << b << " " << c << " " << d << edl;
}
}
M. Computational Geometry
考虑固定边 bc 后,多边形可以分为两部分,第一部分为 bc 所在的 (k + 1) 边形,第二部分则为 abc 构成的三角形。
第一部分面积可以用鞋带公式来做,第二部分可以用指针来维护,这是凸多边形所以当bc逆时针动时,最优的a点也一定逆时针动或不动。
struct QWQ
{
ll x,y;
}a[N];
long long sum3(QWQ A,QWQ B,QWQ C)
{
ll ok = 0;
ok = A.x * B.y + B.x * C.y + C.x * A.y - A.y * B.x - B.y * C.x - C.y * A.x;
return ok;
}
void Solve()
{
memset(a,0,sizeof(a));
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
{
cin >> a[i].x >> a[i].y;
}
ll l = 1;
ll r = 1 + m;
long double sum = 0;
for(int i = l; i < r; i ++ )
{
sum += a[i].x * a[i + 1].y;
sum -= a[i].y * a[i + 1].x;
}
sum += a[r].x * a[1].y;
sum -= a[r].y * a[1].x;
long double ans = 0;
ll st;
for(st = r + 1; ; st ++ )
{
st %= mod;
if(!st) st = n;
ll op = st + 1;
op %= n;
if(!op) op = n;
if(sum3(a[l],a[r],a[op]) > sum3(a[l],a[r],a[st])) continue;
else break;
}
ans = max(ans,sum3(a[l],a[r],a[st]) + sum);
for(int i = 1; i <= n; i ++ )
{
ll lxt = l + 1;
lxt %= n;
if(!lxt) lxt = n;
ll rxt = r + 1;
rxt %= n;
if(!rxt) rxt = n;
sum -= a[r].x * a[l].y;
sum -= a[l].x * a[lxt].y;
sum += a[r].x * a[rxt].y;
sum += a[rxt].x * a[lxt].y;
sum += a[r].y * a[l].x;
sum += a[l].y * a[lxt].x;
sum -= a[r].y * a[rxt].x;
sum -= a[rxt].y * a[lxt].x;
l ++;
r ++;
l %= n;
r %= n;
if(!l) l = n;
if(!r) r = n;
ll op = st + 1;
op %= n;
if(op == 0) op = n;
while(sum3(a[l],a[r],a[st]) < sum3(a[l],a[r],a[op]))
{
st = op;
op ++;
op %= n;
if(op == 0) op = n;
}
ans = max(ans,sum3(a[l],a[r],a[st]) + sum);
}
cout << SPO(12) << ans / 2.0 << edl;
}