(1)题意
高桥每天在S点钟打开他房间的灯,并在T点钟关灯,指示灯亮起时,日期可能会发生改变,判断是否在X点过后30分时亮着。
(2)思路
直接模拟即可。
(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;
void solve()
{
int s,t,x;
cin >> s >> t >> x;
for(int i = s;i != t;i ++) {
if(i == 24) i = 0;
if(x == i) {
cout << "Yes";
return;
}
}
cout << "No";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
B - Takahashi's Secret (atcoder.jp)
(1)题意
高桥有N个朋友,刚开始x这个朋友这个人会知道这个秘密,然后每个人在第i个位置会告诉p[i]这个位置的人高桥的秘密,问最后高桥的朋友会有多少知道秘密。
(2)思路
对于X这个位置,一直往p[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 = 2e5 + 10;
int p[N],vis[N];
void solve()
{
int n,x;
cin >> n >> x;
rep(i,1,n) cin >> p[i];
int c = 0;
while(!vis[x]) {
vis[x] = true;
c ++;
x = p[x];
}
cout << c;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
(1)题意
N名学生参加四天考试,每天考试总分300分,现在前三天考试已经结束,让你预测每个学生的总分的排名是否是Top K。
(2)思路
直接把前三门成绩全部相加,然后排个序,对于每个人直接+300看能否达到Top K即可。
(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;
ll p[N],z[N];
void solve()
{
int n,k;
cin >> n >> k;
int x;
rep(i,1,n) {
rep(j,1,3) {
cin >> x;
p[i] += x;
}
z[i] = p[i];
}
sort(z + 1,z + 1 + n);
rep(i,1,n) {
int idx = upper_bound(z + 1,z + 1 + n,p[i] + 300) - z;
if(n + 1 - idx < k) cout << "Yes\n";
else 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;
}
D - Linear Probing (atcoder.jp)
(1)题意
有一个长为2^20的序列,初始全部位置为-1,对于第Q个查询,会有两种类型。
第一种类型,定义一个Xi,若Axi%N这个位置不为-1,则往下找到第一个不为-1的数组,把那个位置值设置为Xi。第二种类型就是输出低Xi这个位置值是多少。
(2)思路
用一个set维护一下每个为-1的位置,对于第一个类型的查询,我们直接二分一下那个位置即可。
(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 = 2e6 + 10;
ll v[N];
void solve()
{
set<int> st;
int n = (1 << 20) - 1;
rep(i,0,n) {
v[i] = -1;
st.insert(i);
}
int q;
n ++;
cin >> q;
while(q --) {
ll ti,xi;
cin >> ti >> xi;
if(ti == 2) cout << v[xi % n] << '\n';
else {
int cur = xi % n;
auto it = st.lower_bound(cur);
if(it != st.end()) {
v[*it] = xi;
st.erase(it);
}
else {
it = st.lower_bound(0);
v[*it] = xi;
st.erase(it);
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
E - Integer Sequence Fair (atcoder.jp)
(1)题意
计算。
(2)思路
考虑先计算上面部分的指数,若不要对下面M产生影响,我们可以使用欧拉降幂,对于进行快速幂取模998244352即可,然后用下面的再计算一次快速幂取模998244353即可。
(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;
const ll mod = 998244353;
using i128 = __int128;
ll ksm(ll a,ll b,ll p)
{
if(!a) return 0;
i128 rs = 1;
while(b) {
if(b & 1) rs = rs % p * a % p;
a = (i128)a % p * a % p;
b >>= 1;
}
return (ll)rs;
}
void solve()
{
ll n,k,m;
cin >> n >> k >> m;
k %= (mod - 1),m %= (mod);
ll z = ksm(k,n,mod - 1);
cout << ksm(m,z,mod) << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
(1)题意
给你个H*W的矩形,每个格子里面有一个数,问你选择一个H1*W1的矩形去掉一个H2*W2的矩形后,最多能取多大的值。
(2)思路
二维RMQ问题,因此直接上二维ST即可。
(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 = 1003;
ll s[N][N],a[N][N],val[N][N];
ll dp[N][N][11][11];
int num[N];
void rmq(int n, int m){
int nn = log2(n), mm = log2(m);
for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) dp[i][j][0][0] = val[i][j];
for(int ii=0; ii<=nn; ii++)
for(int jj=0; jj<=mm; jj++)
if(ii + jj) {
for(int i=1; i+(1<<ii)-1 <= n; i++)
for(int j=1; j+(1<<jj)-1 <= m; j++) {
if(ii) dp[i][j][ii][jj] = max(dp[i][j][ii-1][jj], dp[i+(1<<(ii-1))][j][ii-1][jj]);
else dp[i][j][ii][jj] = max(dp[i][j][ii][jj-1], dp[i][j+(1<<(jj-1))][ii][jj-1]);
}
}
}
ll query(int x1, int y1, int x2, int y2){
// ll res = 0;
// rep(i,x1,x2) rep(j,y1,y2) {
// res = max(res,val[i][j]);
// }
// return res;
int k1 = log2(x2-x1+1); //num[x2-x1+1];
int k2 = log2(y2-y1+1); //num[y2-y1+1];
x2 = x2 - (1<<k1) + 1;
y2 = y2 - (1<<k2) + 1;
ll ans1 = max(dp[x1][y1][k1][k2], dp[x1][y2][k1][k2]);
ll ans2 = max(dp[x2][y1][k1][k2], dp[x2][y2][k1][k2]);
return max(ans1, ans2);
}
ll get(int x1,int y1,int x2,int y2)
{
return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
}
void solve()
{
int n,m,h1,h2,w1,w2;
cin >> n >> m >> h1 >> w1 >> h2 >> w2;
h2 = min(h2,h1),w2 = min(w2,w1);
rep(i,1,n) {
rep(j,1,m) {
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] + a[i][j] - s[i - 1][j - 1];
}
}
rep(i,h2,n) rep(j,w2,m) {
val[i][j] = get(i - h2 + 1,j - w2 + 1,i,j);
}
rmq(n,m);
ll Ans = 0;
rep(i,h1,n) rep(j,w1,m) {
ll s1 = get(i - h1 + 1,j - w1 + 1,i,j);
ll s2 = query(i - (h1 - h2),j - (w1 - w2),i,j);
Ans = max(Ans,s1 - s2);
}
cout << Ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
G - Digits on Grid (atcoder.jp)
(1)题意
有一个H*W的网格,高桥每次可以把一个棋子移到同一行的某一个位置,青木每次会把棋子移动到同一列的某一个位置,在移动2*N步后,会产生多少个不同的数字序列。
(2)思路
对于这些操作来说,实际上是在二分图上走,把行列抽象成不同点,那我们分别对每一步进行dp,定义dp[S]表示目前经过的某一个数字会到达哪些可用行/列,初始化dp[(1 << H) - 1] = 1。
(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 = 11;
int a[N][N];
vector<int> row[N][N],col[N][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,k;
cin >> n >> m >> k;
k <<= 1;
rep(i,0,n - 1) rep(j,0,m - 1) {
char c;
cin >> c;
a[i][j] = c - '0';
}
rep(i,0,n - 1) rep(j,0,m - 1) {
row[i][a[i][j]].pb(j);
col[j][a[i][j]].pb(i);
}
vector<Mint> dp(1 << n,0);
dp[(1 << n) - 1] = 1;
rep(i,1,k) {
if(i & 1) {
vector<Mint> ndp(1 << m,0);
rep(S,1,(1 << n) - 1) {
if(!dp[S].val()) continue;
rep(dig,1,9) {
int T = 0;
rep(j,0,n - 1) {
if(!(S >> j & 1)) continue;
for(auto p : row[j][dig]) T |= 1 << p;
}
ndp[T] += dp[S];
}
}
dp = ndp;
}
else {
vector<Mint> ndp(1 << n,0);
rep(S,1,(1 << m) - 1) {
if(!dp[S].val()) continue;
rep(dig,1,9) {
int T = 0;
rep(j,0,m - 1) {
if(!(S >> j & 1)) continue;
for(auto p : col[j][dig]) T |= 1 << p;
}
ndp[T] += dp[S];
}
}
dp = ndp;
}
}
Mint Ans = 0;
for(int i = 1;i < (1 << n);i ++) {
Ans += dp[i];
}
cout << Ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}
(1)题意
给你两个长度为N的整数序列A和C,你可以操作任意次,每次选择一个整数i,使得A[i] + 1,花费Ci元,完成操作后你需要支付K*X元,K是A元素中不同值得个数。
(2)思路
我们对于每一个序列按A为第一关键字排序。考虑某一段数把前面得都变成当前位置得值,那么这个题就转换成了把这个序列划分成多少段得问题。
因此考虑dp[i]表示把前i段划分好后,最少需要花费多少元?
化简一下
考虑前缀和维护sumC和sumCA,对于有j得项抽出来。
设dp[j] + sumCA[j]为b -sumC[j]为k
发现这个东西可以用斜率优化掉,因此直接上李超树即可。
(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;
namespace OY {
struct LichaoSegLine {
double m_k, m_b;
LichaoSegLine() = default;
LichaoSegLine(double k, double b) : m_k(k), m_b(b) {}
double calc(int i) const { return m_k * i + m_b; }
};
template <typename Line>
struct LichaoSegLess {
bool operator()(const Line &x, const Line &y, int i) const { return x.calc(i) < y.calc(i); }
};
template <typename Line = LichaoSegLine, typename Compare = LichaoSegLess<Line>>
struct LichaoSegTree {
struct LineNode {
Line m_line;
LineNode *m_lchild, *m_rchild;
LineNode(Line line, LineNode *lchild = nullptr, LineNode *rchild = nullptr) : m_line(line), m_lchild(lchild), m_rchild(rchild) {}
};
Line m_defaultLine;
uint64_t m_length;
Compare m_comp;
LineNode *m_root;
void _clear(LineNode *p) {
// if (p->m_lchild) _clear(p->m_lchild);
// if (p->m_rchild) _clear(p->m_rchild);
// delete p;
}
LichaoSegTree(uint64_t length = 0, Compare comp = Compare(), Line defaultLine = Line()) : m_comp(comp), m_defaultLine(defaultLine), m_root(nullptr) { resize(length); }
~LichaoSegTree() {
if (m_root) _clear(m_root);
}
void resize(uint64_t length) {
if (m_root) _clear(m_root);
m_root = (m_length = length) ? new LineNode(m_defaultLine, nullptr, nullptr) : nullptr;
}
void add(uint64_t left, uint64_t right, const Line &line) {
if (right >= m_length) right = m_length - 1;
if (left > right) return;
auto dfs = [&](auto self, LineNode *&cur, uint64_t lbound, uint64_t rbound, Line line) -> void {
if (!cur) cur = new LineNode(m_defaultLine, nullptr, nullptr);
if (uint64_t mid = (lbound + rbound) / 2; left <= lbound && right >= rbound) {
if (m_comp(cur->m_line, line, mid)) std::swap(cur->m_line, line);
if (lbound < rbound)
if (m_comp(cur->m_line, line, lbound))
self(self, cur->m_lchild, lbound, mid, line);
else if (m_comp(cur->m_line, line, rbound))
self(self, cur->m_rchild, mid + 1, rbound, line);
} else {
if (left <= mid) self(self, cur->m_lchild, lbound, mid, line);
if (right > mid) self(self, cur->m_rchild, mid + 1, rbound, line);
}
};
dfs(dfs, m_root, 0, m_length - 1, line);
}
Line query(uint64_t i) const {
Line res(m_defaultLine);
auto dfs = [&](auto self, LineNode *cur, uint64_t lbound, uint64_t rbound) -> void {
if (!cur) return;
if (m_comp(res, cur->m_line, i))
res = cur->m_line;
if (lbound == rbound) return;
if (uint64_t mid = (lbound + rbound) / 2; i <= mid)
self(self, cur->m_lchild, lbound, mid);
else
self(self, cur->m_rchild, mid + 1, rbound);
};
dfs(dfs, m_root, 0, m_length - 1);
return res;
}
};
template <typename Line = LichaoSegLine, typename Compare = LichaoSegLess<Line>>
LichaoSegTree(uint64_t = 0, Compare = Compare(), Line = Line()) -> LichaoSegTree<Line, Compare>;
}
using i128 = __int128;
struct Line {
ll k,b;
ll calc(ll i) const {
return i * k + b;
}
};
struct compare {
bool operator()(const Line &x, const Line &y, i128 i) const {
ll x_val = x.calc(i), y_val = y.calc(i);
return x_val > y_val;
}
};
const uint64_t inf = 1e12;
pair<ll,ll> e[N];
ll sumC[N],sumCA[N];
ll dp[N];
void solve()
{
int n,x;
cin >> n >> x;
//dp[i]:前i个的最小花费
//dp[i] = dp[j] + k:[j + 1,i](a[i] - a[k])*c[k] + x
//dp[i] = dp[j] + a[i] * k:[j + 1,i]c[k] - k:[j + 1,i]:a[k] * c[k] + x
//dp[i] = dp[j] + a[i] * (sumC[i] - sumC[j]) - sumCA[i] + sumCA[j] + x
//dp[i] = dp[j] + a[i] * sumC[i] - a[i] * sumC[j] - sumCA[i] + sumCA[j] + x
//设dp[j] + sumCA[j]为b -sumC[j]为k
//则dp[i] = b + a[i] * sumC[i] - a[i] * k - sumCA[i] + x
//根据a[i] query一个最小的值进行转移即可
OY::LichaoSegTree tr(1000005,compare(),Line{0,inf});
for(int i = 1;i <= n;i ++) {
cin >> e[i].fi >> e[i].se;
}
sort(e + 1,e + 1 + n);
for(int i = 1;i <= n;i ++) {
sumC[i] = sumC[i - 1] + e[i].se;
sumCA[i] = sumCA[i - 1] + 1ll * e[i].fi * e[i].se;
}
tr.add(0,inf,{0,0});
for(int i = 1;i <= n;i ++) {
dp[i] = 1ll * e[i].fi * sumC[i] + tr.query(e[i].fi).calc(e[i].fi) - sumCA[i] + x;
tr.add(0,inf,{-sumC[i],dp[i] + sumCA[i]});
}
cout << dp[n] << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T = 1;
// cin >> T;
while(T --) solve();
return 0;
}