D.Tasks - AtCoder Beginner Contest 319
(1)题意
给你一个M行得框框和N个单词,每个单词有一个宽度,每个单词之间应该用一个空格隔开,首位单词不需要,问至少需要多宽才能使得单词不会超过M行。
(2)解题思路
直接二分宽度,然后check即可。
(3)代码实现
#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int L[N];
int n,m;
bool check(ll k)
{
int cnt = 0;
ll cur = 0;
rep(i,1,n) {
if(k < L[i]) {
return false;
}
}
rep(i,1,n) {
if(cur - L[i] - 1 >= 0) {
cur -= L[i] + 1;
}
else {
cur = k - L[i];
cnt ++;
}
}
// cout << "???" << k << '\n';
return cnt <= m;
}
void solve()
{
cin >> n >> m;
for(int i = 1;i <= n;i ++) {
cin >> L[i];
}
ll l = 1,r = 1e18;
while(l <= r) {
ll mid = (l + r) >> 1;
if(check(mid)) r = mid - 1;
else l = mid + 1;
}
cout << l << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
(1)题意
A要从自己家到B家,中间得路段构成是A先走一段路到达站点1,然后做公交到站点n,最后走一段路到B家,每一个站点得公交车会在P[i]倍数得时间发车,需要T[i]得时间才能到达下一个站,现在有q个询问,每个询问给定你起始时间,问最后达到B家得最小时间是多少。
(2)解题思路
考虑每一个P[i]得倍数都会有一些余数,不妨把他们得最小公倍数求出来,然后预处理出0-最小公倍数得起始时间需要花费多少时间到达B家。
(3)代码实现
#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int p[N],t[N];
ll Ans[N];
int n,x,y;
ll calc(ll st)
{
st += x;
for(int i = 1;i < n;i ++) {
int z = st % p[i];
if(!z) z = p[i];
st += p[i] - z;
st += t[i];
}
return st + y;
}
void solve()
{
cin >> n >> x >> y;
set<int> pt;
for(int i = 1;i < n;i ++) {
cin >> p[i] >> t[i];
pt.insert(p[i]);
}
int cur = 0;
for(auto x : pt) {
if(!cur) cur = x;
else cur = cur * x / __gcd(cur,x);
}
for(int i = 0;i < cur;i ++) {
Ans[i] = calc(i);
}
int q;
cin >> q;
for(int i = 1;i <= q;i ++) {
ll v;
cin >> v;
int p = v % cur;
cout << v - p + Ans[p] << '\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
F - Fighter Takahashi (atcoder.jp)
(1)题意
你在一颗树上战斗,起初你的战斗力是1,树上每个点要么是药剂,要么是怪兽,如果是怪兽,你的战斗力大于等于他就可以击败他获取奖励战斗力,如果是药剂你可以使你得战斗力翻g[i]倍,现在问你是否可以从1号节点战败所有怪兽。
(2)解题思路
首先,若是你当前得实力够打怪兽,那么一定是先打得,如果不够了才考虑用药剂。那么一个很明显得思路是,确定一个吃药得顺序,然后bfs一遍看看能不能打完所有怪兽,那么时间复杂度显然是不太行得。所以我们考虑使用状压dp优化这个枚举吃药顺序得过程,状态dp[i]表示吃了i这些药最高能到得战斗力是多少,最后判一下是否大于等于最高战斗力得怪兽即可。
(3)代码实现
#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 505;
int t[N],s[N],g[N],p[N];
vector<int> e[N];
ll dp[1<<(10)];
void solve()
{
int n,tot = 0;
cin >> n;
vector<int> med,pos(n + 1,0);
int master = 0;
rep(i,2,n) {
cin >> p[i] >> t[i] >> s[i] >> g[i];
if(t[i] == 2) {
med.pb(i);
pos[i] = sz(med) - 1;
tot ++;
}
master = max(master,s[i]);
e[p[i]].pb(i);
}
memset(dp,-1,sizeof(dp));
//dp[i]:表示吃了这些药能达到最大得能量值
dp[0] = 1;
rep(i,0,(1<<tot)-1) {
if(dp[i] != -1) {
priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>> q;
q.push({-1,1});
vector<bool> used(sz(med),false);
int cnt = 0;
ll S = 0;
while(!q.empty()) {
auto [val,ver] = q.top();
q.pop();
if(val > dp[i]) break;
cnt ++;
if(t[ver] == 2 && !(i >> pos[ver] & 1)) {
used[pos[ver]] = true;
continue;
}
if(t[ver] == 1) {
dp[i] += g[ver];
S += g[ver];
}
for(auto v : e[ver]) {
if(t[v] == 2) q.push({-1,v});
else q.push({s[v],v});
}
}
// cout << i << ' ' << dp[i] << '\n';
if(dp[i] >= master || cnt == n) {
cout << "Yes" << '\n';
return;
}
rep(j,0,tot-1) {
if(used[j]) {
dp[i | (1 << j)] = max(dp[i | (1 << j)],dp[i] * g[med[j]] - S);
}
}
}
}
cout << "No" << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
G - Counting Shortest Paths (atcoder.jp)
(1)题意
给你一个完全图,删除一些边后,问你是否能从1-N,如果能,请给出最短路径数量。
(2)解题思路
P1. 首先对于最短路径,我们考虑01bfs得特性,一旦有一个点进入了则不会再进入一次,因此我们可以用一个set维护一下未进入得点集,每次对于一个进入得点,扫描一下未进入得点集,若这个点和进入得点之间得边未删除,则可以把这个点加入队列,这样就可以很快得求得最短路径。
P2.现在我们需要考虑路径数量得问题了,考虑将图分成如下形式。
考虑dp[i],表示i为最短路径上的点的方案数是多少。那么显然对于点集为2的点dp[i]等价于所有点集为1的点的方案和-i不能到达的点集为1的点的方案,依次类推求出dp[n]即可。
(3)代码实现
#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
set<int> e[N],g[N],pos;
int dis[N];
using i64 = long long;
constexpr int P = 998244353;
// assume -P <= x < 2P
int Vnorm(int x) {
if (x < 0) {
x += P;
}
if (x >= P) {
x -= P;
}
return x;
}
template<class T>
T power(T a, i64 b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
struct Mint {
int x;
Mint(int x = 0) : x(Vnorm(x)) {}
Mint(i64 x) : x(Vnorm(x % P)) {}
int val() const {
return x;
}
Mint operator-() const {
return Mint(Vnorm(P - x));
}
Mint inv() const {
assert(x != 0);
return power(*this, P - 2);
}
Mint &operator*=(const Mint &rhs) {
x = i64(x) * rhs.x % P;
return *this;
}
Mint &operator+=(const Mint &rhs) {
x = Vnorm(x + rhs.x);
return *this;
}
Mint &operator-=(const Mint &rhs) {
x = Vnorm(x - rhs.x);
return *this;
}
Mint &operator/=(const Mint &rhs) {
return *this *= rhs.inv();
}
friend Mint operator*(const Mint &lhs, const Mint &rhs) {
Mint res = lhs;
res *= rhs;
return res;
}
friend Mint operator+(const Mint &lhs, const Mint &rhs) {
Mint res = lhs;
res += rhs;
return res;
}
friend Mint operator-(const Mint &lhs, const Mint &rhs) {
Mint res = lhs;
res -= rhs;
return res;
}
friend Mint operator/(const Mint &lhs, const Mint &rhs) {
Mint res = lhs;
res /= rhs;
return res;
}
friend std::istream &operator>>(std::istream &is, Mint &a) {
i64 v;
is >> v;
a = Mint(v);
return is;
}
friend std::ostream &operator<<(std::ostream &os, const Mint &a) {
return os << a.val();
}
};
void solve()
{
int n,m;
cin >> n >> m;
for(int i = 1;i <= m;i ++) {
int u,v;
cin >> u >> v;
e[u].insert(v);
e[v].insert(u);
}
for(int i = 2;i <= n;i ++) {
pos.insert(i);
}
memset(dis,0x3f,sizeof(dis));
dis[1] = 0;
queue<int> q;
q.push(1);
while(!q.empty()) {
int v = q.front();
q.pop();
auto nps = pos;
for(auto x : nps) {
if(!e[v].count(x) && pos.count(x)) {
dis[x] = dis[v] + 1;
pos.erase(x);
q.push(x);
}
}
}
if(dis[n] >= n) {
cout << -1 << '\n';
return;
}
for(int i = 1;i <= n;i ++) {
if(dis[i] <= n) g[dis[i]].insert(i);
}
vector<Mint> dp(n + 1,0),s(n + 1,0);
dp[1] = 1;
for(int i = 0;i < n;i ++) {
if(i >= 1) {
for(auto x : g[i]) {
dp[x] += s[i];
}
}
for(auto x : g[i]) {
s[i + 1] += dp[x];
for(auto y : e[x]) {
if(dis[y] == dis[x] + 1) {
dp[y] -= dp[x];
}
}
}
}
cout << dp[n];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}