HOJ1034遭遇(2019.9.19模拟赛A组T1)
题意
lky要跳楼
你需要一开始选择一座楼,开始跳楼。在第(i)座楼准备跳楼需要(c_i)的花费。每次可以跳到任何一个还没有跳过的楼上去。但跳楼是有代价的,每次跳到另外一座楼的代价是两座楼高度的差的绝对值,最后一次从楼上跳到地面上不需要代价(只能跳到地上一次)。求在代价不超过(T)的情况下,最多跳几次楼。(一座楼只能跳一次,且每次跳楼都要计算准备的花费)
怎么做
贪心+DP
由题意可知,每次跳跃要消耗高度差的代价。这样如果反复横跳肯定是不优的,为了保证高度差的代价尽量小,我们应采用按照从高到低或从低到高的顺序跳楼。
f[i][j]跳了j栋到i的最小代价
DP阶段:跳到了i
状态:跳了j栋
转移方程:f[i][j] = min(f[i][j], f[k][j-1]);
复杂度(O(n^3))
其实是可以优化成(O(n^2))的,只是我不会。又要咕咕咕这个做法了
代码
#include <bits/stdc++.h>
namespace fdata
{
inline char nextchar()
{
static const int BS = 1 << 21;
static char buf[BS], *st, *ed;
if (st == ed)
ed = buf + fread(st = buf, 1, BS, stdin);
return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename T>
inline T poread()
{
T ret = 0;
char ch;
while (!isdigit(ch = nextchar()))
;
do
ret = ret * 10 + ch - '0';
while (isdigit(ch = nextchar()));
return ret;
}
template <typename Y>
inline void poread(Y &ret)
{
ret = 0;
char ch;
while (!isdigit(ch = nextchar()))
;
do
ret = ret * 10 + ch - '0';
while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
using namespace std;
#define min(_x, _y) (_x > _y ? _y : _x)
const int MAXN = 305;
int f[MAXN][MAXN];
struct node
{
int h, c;
bool operator<(const node &it) const
{
return h < it.h;
}
} building[MAXN];
#define h(_o) building[_o].h
#define c(_o) building[_o].c
int n, t;
int main()
{
poread<int>(n);
for (register int i = 1; i <= n; ++i)
poread<int>(building[i].c);
for (register int i = 1; i <= n; ++i)
poread<int>(building[i].h);
poread<int>(t);
sort(building + 1, building + 1 + n);
memset(f, 0x3f, sizeof(f));
for (register int i = 1; i <= n; ++i)
f[i][0] = 0;
for (register int i = 1; i <= n; ++i)
{
for (register int j = 1; j <= i + 1; ++j)
{
for (register int k = 1; k < i; ++k)
{
f[i][j] = min(f[i][j], f[k][j - 1] + h(i) - h(k) + c(k));
}
}
}
int ans = 0;
for (register int i = n; ~i; --i)
{
for (register int j = 1; j <= n; ++j)
{
if (f[j][i] + c(j) <= t)
{
printf("%d
", i + 1);
return 0;
}
}
}
}
HOJ1035都市(2019.9.19模拟赛A组T2)(Luogu3540SQU-Squarks)
(color{blue}{ ext{404nofind}})
题意
有n个数,给出两两之间的和,求这n个数的所有可能值。
怎么做
这是智商题
考虑一个事情,如果给定三个数(a_1, a_2, a_3)分别相加的和,怎样求出这三个数呢?
显然,我们可以算出来这三个数的和,即(frac{(a_1 + a_2)+(a_1+a_3)+(a_2+a_3)}{2} = a_1+a_2+a_3),并依次相减,即可得到(a_1, a_2, a_3)。
那么对于(n)个数{(a_1, a_2...a_n)}, 若我们钦定数列是有序的,我们可以判断出(a_1+a_2)以及(a_1+a_3),因为这两个分别是最小的以及第二小的数。
证明:
假设(a_1+a_2>a_x+a_y(x, y eq 1, 2))
(ecause a_2<a_x)
( herefore a_1+a_2>a_2+a_y)
(qquad quad a_1 > a_y)
这与{(a_1, a_2...a_n)}有序不符,所以(a_1+a_2)是最小的,同理可证(a_1+a_3)第二小。
此时我们已经知道了(a_1+a_2)以及(a_1+a_3),在剩下的情况中枚举(a_2+a_3),就可以求出(a_1, a_2, a_3)这三个数。
在求得(a_1)后,我们便可枚举(a_1+a_k)是哪个数,并且判断该情况是否合法。以上两个枚举复杂度都是O((n)).
关于如何判断是否合法,可以通过已知的数,进行求和,并判断这个情况下的和是否符合题目给出的值。这个复杂度是O((n))的。
整体复杂度O((n^3))。
放代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 305;
int n, m, cnt;
int a[45005];
bool use[45005];
int num[MAXN];
struct node
{
int ans[MAXN];
inline void print()
{
for (register int i = 1; i <= n; ++i)
printf("%d ", ans[i]);
putchar('
');
}
void operator=(int *s)
{
memcpy(ans, s, sizeof(ans));
}
} ans[MAXN];
inline int find(const int &x)
{
int l = 1, r = m, mid, res;
while (l <= r)
{
mid = (l + r) >> 1;
if (a[mid] <= x)
{
res = mid, l = mid + 1;
}
else
{
r = mid - 1;
}
}
return res;
}
inline void check(int x)
{
memset(use, 0, sizeof(use));
int sum = a[1] + a[2] + a[x];
if (sum & 1)
return;
sum >>= 1;
num[1] = sum - a[x];
num[2] = sum - a[2];
num[3] = sum - a[1];
use[1] = use[2] = use[x] = true;
for (register int i = 3, k = 4; k <= n; ++k) //枚举a1 + ak
{
while (i <= m && use[i]) //判重
++i;
if (i > m)
return;
num[k] = a[i] - num[1];
use[i] = true;
for (register int j = 2; j < k; ++j) //枚举a[x] + a[k]
{
if (num[j] > num[k]) //保证顺序
return;
sum = num[j] + num[k];
register int p = find(sum);
if (a[p] != sum)
return;
register int px = p;
while (px && a[px] == a[p]) //找到相同数值中最前面的一个
--px;
++px;
while (px <= m && use[px] && a[px] == a[p])
++px;
if (a[px] != a[p] || use[px])
return;
p = px;
use[p] = true;
}
}
++cnt;
ans[cnt] = num;
}
int main()
{
scanf("%d", &n);
m = (n * (n - 1)) / 2;
for (register int i = 1; i <= m; ++i)
{
scanf("%d", &a[i]);
}
sort(a + 1, a + m + 1);
for (register int i = 3; i <= m;) //枚举a2 + a3;
{
check(i);
register int ii = i;
while (a[ii] == a[i]) //去重
++ii;
i = ii;
}
printf("%d
", cnt);
for (register int i = 1; i <= cnt; ++i)
ans[i].print();
}
小结
考试的时候这个题只想到了通过三个数的和求出这三个数,应该考虑将其扩展进行枚举。
HOJ1036街灯(2019.9.19模拟赛A组T3)
题意
街上的街灯亮起,指引向着远方的路。每个街灯上都有一个数,每次询问,第(l)个街灯到第(r)个街灯上的数模(p)等于(v)的有几个。
咋做
({nsqrt{n}log{n}}) 在线做法
这种题考虑分成部分处理。对于({q<100})的部分,它的模数不超过100,所以可以直接开一个桶(v_1[i][j])记录(i % p == j) 的数的出现位置,询问时查询出这个桶中有几个位置在(l,r)之间即可。由于数据范围较大,这个桶开成vector,保证不炸空间。
对于剩下的部分,我们发现如果({a equiv v{pmod p} }), (a = p * i + v), 我们枚举(i),便可以找到所有符合询问要求的数。所以开一个桶(v_2[x]),放入所有(x)的出现位置,查询时枚举(i), 使 $ x = p*i+v (, 找到在)l,r(区间内有几个)x(,并对所有x的答案求和即为所求。<br>复杂度分析:每个询问,由于枚举)x(是)sqrt{n}(的,查询区间内的数二分左右端点是)log{n}(的,总体复杂度)O({nsqrt{n}log{n}})$。
({nsqrt{n}}) 离线做法
和之前的做法差不多,只是要离线按照位置(sort)一下询问,处理(1) ~ (l-1)和(1)~(r),答案为二者的差。由于不用二分,所以复杂度是({O(nsqrt{n})})
Code
这个是(O({nsqrt{n}log{n}}))在线的
#include <bits/stdc++.h>
namespace fdata
{
inline char nextchar()
{
static const int BS = 1 << 21;
static char buf[BS], *st, *ed;
if (st == ed)
ed = buf + fread(st = buf, 1, BS, stdin);
return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename T>
inline T poread()
{
T ret = 0;
char ch;
while (!isdigit(ch = nextchar()))
;
do
ret = ret * 10 + ch - '0';
while (isdigit(ch = nextchar()));
return ret;
}
template <typename Y>
inline void poread(Y &ret)
{
ret = 0;
char ch;
while (!isdigit(ch = nextchar()))
;
do
ret = ret * 10 + ch - '0';
while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
using namespace std;
const int MAXN = 105;
int n, m;
struct node
{
vector<int> v;
inline int find(const int &x, int &rec)
{
register int l = 0, r = v.size() - 1, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if (v[mid] >= x)
{
rec = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
return rec;
}
inline int finds(const int &x, int &rec)
{
register int l = 0, r = v.size() - 1, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if (v[mid] <= x)
{
rec = mid;
l = mid + 1;
}
else
{
r = mid - 1;
}
}
return rec;
}
inline void push_back(const int &x)
{
v.push_back(x);
}
} v1[MAXN][MAXN], v2[10005];
int a[100005];
int main()
{
#ifdef lky233
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#endif
poread(n);
poread(m);
for (register int i = 1; i <= n; ++i)
poread(a[i]);
for (register int i = 1; i <= n; ++i)
v2[a[i]].push_back(i);
for (register int i = 1; i <= 100; ++i)
{
for (register int j = 1; j <= n; ++j)
{
v1[i][a[j] % i].push_back(j);
}
}
for (register int i = 1, l, r, p, v; i <= m; ++i)
{
poread(l), poread(r), poread(p), poread(v);
if (p <= 100)
{
int ans1, ans2;
ans1 = ans2 = -1;
v1[p][v].find(l, ans1);
if (ans1 == -1)
{
puts("0");
continue;
}
v1[p][v].finds(r, ans2);
if (ans2 == -1)
{
puts("0");
continue;
}
printf("%d
", ans2 - ans1 + 1);
}
else
{
int pos = v, tot = 0;
while (pos <= 10000)
{
int ans1 = -1, ans2 = -1;
v2[pos].find(l, ans1);
// cerr << i << " " << ans1 << " ";
if (ans1 == -1)
{
pos += p;
continue;
}
v2[pos].finds(r, ans2);
// cerr << ans2 << endl;
if (ans2 == -1)
{
pos += p;
continue;
}
tot += (ans2 - ans1 + 1);
pos += p;
}
printf("%d
", tot);
}
}
return 0;
}
这个是({O(nsqrt{n})})离线的
#include <bits/stdc++.h>
using namespace std;
namespace fdata
{
inline char nextchar()
{
static const int BS = 1 << 21;
static char buf[BS], *st, *ed;
if (st == ed)
ed = buf + fread(st = buf, 1, BS, stdin);
return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename T>
inline T poread()
{
T ret = 0;
char ch;
while (!isdigit(ch = nextchar()))
;
do
ret = ret * 10 + ch - '0';
while (isdigit(ch = nextchar()));
return ret;
}
template <typename Y>
inline void poread(Y &ret)
{
ret = 0;
char ch;
while (!isdigit(ch = nextchar()))
;
do
ret = ret * 10 + ch - '0';
while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
const int MAXN = 1e5 + 5;
int n, m;
int a[MAXN];
int c[105][105];
int cc[10005];
struct node
{
int p, v, id;
bool op;
node() {}
node(int _p, int _v, int _id, int _op) { p = _p, v = _v, id = _id, op = _op; }
} que[MAXN];
int nxt[MAXN << 1], head[MAXN << 1], tot;
int ans[MAXN][2];
int main()
{
#ifdef lky233
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#endif
poread(n), poread(m);
for (register int i = 1; i <= n; ++i)
poread(a[i]);
for (register int i = 1, l, r, p, v; i <= m; ++i)
{
poread(l), poread(r), poread(p), poread(v);
que[++tot] = node(p, v, i, 0);
nxt[tot] = head[l - 1];
head[l - 1] = tot;
que[++tot] = node(p, v, i, 1);
nxt[tot] = head[r];
head[r] = tot;
}
for (register int I = 1; I <= n; ++I)
{
++cc[a[I]];
for (register int j = 1; j <= 100; ++j)
++c[j][a[I] % j];
for (register int i = head[I]; i; i = nxt[i])
{
register int p = que[i].p, v = que[i].v;
if (p <= 100)
{
ans[que[i].id][que[i].op] = c[p][v];
}
else
{
register int tmp = 0;
for (register int j = v; j <= 10000; j += p)
tmp += cc[j];
ans[que[i].id][que[i].op] = tmp;
}
}
}
for (register int i = 1; i <= m; ++i)
printf("%d", ans[i][1] - ans[i][0]), putchar('
');
}
附赠随机数据生成器。。。对拍好久。。。
#include <bits/stdc++.h>
using namespace std;
int seed;
int main()
{
freopen("testdata.out", "r", stdin);
cin >> seed;
srand(seed + time(0));
freopen("testdata.in", "w", stdout);
int n = (rand() - 1) % 20 + 1;
int m = (rand() - 1) % 10 + 1;
cout << n << " " << m << endl;
for (register int i = 1; i <= n; ++i)
{
cout << ((rand() - 1) % 100 + 1) << " ";
}
cout << endl;
for (register int i = 1; i <= m; ++i)
{
int l = (rand() - 1) % n + 1;
int r = (rand() - 1) % n + 1;
int p = (rand() - 1) % (200) + 1;
int v = (rand() - 1) % n + 1;
if (l > r)
swap(l, r);
cout << l << " " << r << " " << p << " " << v << endl;
}
}