A. Buy a Shovel
有单价为k的商品和1个r面额的硬币,输出最小的不需要找零的最小购买件数。for就行了。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
typedef __int64 LL;
typedef pair<int,int> PII;
#define mp make_pair
#define pb push_back
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
#define lowbit(x) ((x)&(-(x)))
#define bitcnt(x) __builtin_popcount(x)
#define bitcntll(x) __builtin_popcountll(x)
#define debug puts("-------------");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int MAXN = 2e2 + 50;
const int MAXM = 2e4 + 50;
int k, r;
int main() {
#ifdef LOCAL_NORTH
// FIN;
#endif // LOCAL_NORTH
while (~scanf("%d%d", &k, &r)) {
int ans = 0;
for (int i = 1; ; i++) {
if (k * i % 10 == r || k * i % 10 == 0) {
printf("%d\n", i);
break;
}
}
}
#ifdef LOCAL_NORTH
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // LOCAL_NORTH
return 0;
}
B. Cormen — The Best Friend Of a Man
增大一个序列中的某些项,使得增加后的序列的相连两项和为k。输出增加的值的和。
贪心着搞就行了,第一天不变,之后的每一项都根据前一项来确定增加多少,然后输出和。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
typedef __int64 LL;
typedef pair<int,int> PII;
#define mp make_pair
#define pb push_back
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
#define lowbit(x) ((x)&(-(x)))
#define bitcnt(x) __builtin_popcount(x)
#define bitcntll(x) __builtin_popcountll(x)
#define debug puts("-------------");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int MAXN = 5e2 + 50;
const int MAXM = 2e4 + 50;
int n, k, a[MAXN];
int main() {
#ifdef LOCAL_NORTH
FIN;
#endif // LOCAL_NORTH
while (~scanf("%d%d", &n, &k)) {
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int ans = 0;
for (int i = 2; i <= n; i++) {
if (a[i] < k - a[i - 1]) {
ans += k - a[i - 1] - a[i];
a[i] = k - a[i - 1];
}
}
printf("%d\n", ans);
for (int i = 1; i <= n; i++) printf("%d%c", a[i], " \n"[i == n]);
}
#ifdef LOCAL_NORTH
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // LOCAL_NORTH
return 0;
}
C. Sanatorium
给定b,d,s分别为一个人吃早餐,中餐(可以这么理解)和晚餐的次数,输出这个人最少有几顿饭没吃。note:这个人可能在一天中的任意时间到达,比如晚饭之前,这样他就吃上晚饭,所以0 0 1这组数据的答案是0。
首先假设在早饭之前到达,取b,d,s中最大的,作为最后一天,然后就可以计算一个答案。
然后s--,也就是假设晚饭之前到达,可以计算出一个答案。
最后d--,假设中饭之前到达,计算出一个答案。
然后3个答案取最小值。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
typedef __int64 LL;
typedef pair<int,int> PII;
#define mp make_pair
#define pb push_back
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
#define lowbit(x) ((x)&(-(x)))
#define bitcnt(x) __builtin_popcount(x)
#define bitcntll(x) __builtin_popcountll(x)
#define debug puts("-------------");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int MAXN = 5e2 + 50;
const int MAXM = 2e4 + 50;
LL b, d, s;
int main() {
#ifdef LOCAL_NORTH
FIN;
#endif // LOCAL_NORTH
while (~scanf("%I64d%I64d%I64d", &b, &d, &s)) {
LL maxx = max(b, max(d, s));
LL ans = 0;
ans += max(maxx - 1 - b, 0LL);
ans += max(maxx - 1 - d, 0LL);
ans += max(maxx - 1 - s, 0LL);
if (s == maxx) ans += (s == b ? 0 : 1) + (s == d ? 0 : 1);
else if (d == maxx) ans += (b == d ? 0 : 1);
s--;
LL maxx1 = max(b, max(d, s));
LL ans1 = 0;
ans1 += max(maxx1 - 1 - b, 0LL);
ans1 += max(maxx1 - 1 - d, 0LL);
ans1 += max(maxx1 - 1 - s, 0LL);
if (s == maxx1) ans1 += (s == b ? 0 : 1) + (s == d ? 0 : 1);
else if (d == maxx1) ans1 += (b == d ? 0 : 1);
d--;
LL maxx2 = max(b, max(d, s));
LL ans2 = 0;
ans2 += max(maxx2 - 1 - b, 0LL);
ans2 += max(maxx2 - 1 - d, 0LL);
ans2 += max(maxx2 - 1 - s, 0LL);
if (s == maxx2) ans2 += (s == b ? 0 : 1) + (s == d ? 0 : 1);
else if (d == maxx2) ans2 += (b == d ? 0 : 1);
printf("%I64d\n", min(ans, min(ans1, ans2)));
}
#ifdef LOCAL_NORTH
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // LOCAL_NORTH
return 0;
}
D. Exams
有n(n <= 2e5)天和m个科目,要在n天内把m个科目全部考完,每天可以 复习某一科或者进行某一科的考试。
输入d[i],代表第i天只能考科目d[i],d[i]==0代表当天不能考试。
再输入m门科目的复习时间,在科目i考试之前必须花费a[i]复习科目i。
最后输出考完所有科目最短时间,考不完则输出-1。
首先记录一下m门科目的考试时间,然后二分答案,每次检查能否在mid天之前(含mid)完成。
通过二分考试时间数组来找到m门科目在mid之前的最晚考试时间。
定义一个变量来记录可用的复习时间。把科目按照时间从小到大排序开始枚举,每次检查re是否大于等于a[第i个科目的id] + 1,小于0即false,否则re加上从当前科目的考试时间到下一个科目的考试时间。能遍历到m同时re>=0即为true。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
typedef __int64 LL;
typedef pair<int,int> PII;
#define mp make_pair
#define pb push_back
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
#define lowbit(x) ((x)&(-(x)))
#define bitcnt(x) __builtin_popcount(x)
#define bitcntll(x) __builtin_popcountll(x)
#define debug puts("-------------");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 50;
const int MAXM = 2e4 + 50;
int n, m, d[MAXN], a[MAXN];
vector<int> app[MAXN];
struct node{
int id, index;
bool operator < (const node& _) const {return index < _.index;}
}lastApp[MAXN];
bool judge(int len) {
for (int i = 1; i <= m; i++) {
lastApp[i].index = -1;
vector<int>::iterator p = upper_bound(app[i].begin(), app[i].end(), len);
if (p != app[i].begin())
lastApp[i].index = *(p - 1);
lastApp[i].id = i;
}
sort(lastApp + 1, lastApp + m + 1);
int re = lastApp[1].index;
for (int i = 1; i < m; i++) {
re -= a[lastApp[i].id] + 1;
if (re < 0) return false;
re += lastApp[i + 1].index - lastApp[i].index;
}
re -= a[lastApp[m].id] + 1;
if (re < 0) return false;
return true;
}
int main() {
#ifdef LOCAL_NORTH
FIN;
#endif // LOCAL_NORTH
while (~scanf("%d%d", &n, &m)) {
for (int i = 1; i <= n; i++) app[i].clear();
for (int i = 1; i <= n; i++) {
scanf("%d", &d[i]);
app[d[i]].pb(i);
}
for (int i = 1; i <= m; i++) scanf("%d", &a[i]);
int l = 1, h = n, ans = -1;
while (l <= h) {
int mid = (l + h) / 2;
if (judge(mid)) {
ans = mid;
h = mid - 1;
}
else l = mid + 1;
}
printf("%d\n", ans);
}
#ifdef LOCAL_NORTH
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // LOCAL_NORTH
return 0;
}
E. Sockets
n台电脑,m个插头,每个插头可以连接一台电脑,当电脑电压等于插头电压时,电脑可以工作。给出电脑工作电压,插头的电压,现在可以把插头电压变为(原电压+1)/2,。求最多能工作的电脑数c以及使c台电脑工作的最小变换次数。
把电脑安装power排序。定义一个pos指针,指向当前电脑。把每个插头的id、power以及每个插头的变换次数放到优先队列中。
每次取power最大,变换次数最小的插头检查与电脑pos的电压比较,大于pos电压就加一除二重新入队,等于就pop并且pos--,小于重新入队且pos--。
中途记录一下匹配的id。然后就可以输出答案了。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define mp make_pair
#define pb push_back
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
#define lowbit(x) ((x)&(-(x)))
#define bitcnt(x) __builtin_popcount(x)
#define bitcntll(x) __builtin_popcountll(x)
#define debug puts("-------------");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int MAXN = 2e5 + 50;
const int MAXM = 1e4 + 50;
int n, m;
int match[MAXN][2], cnt[MAXN];
struct node{
int id, power;
node() {}
node(int _id, int _power) {
id = _id;
power = _power;
}
bool operator < (const node& _) const {
if (power != _.power)
return power < _.power;
return cnt[id] > cnt[_.id];
}
}p[MAXN], s[MAXN];
int main() {
#ifdef LOCAL_NORTH
FIN;
#endif // LOCAL_NORTH
while (~scanf("%d%d", &n, &m)) {
memset(match, 0, sizeof(match));
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++) {
scanf("%d", &p[i].power);
p[i].id = i;
}
sort(p + 1, p + 1 + n);
priority_queue<node> q;
for (int i = 1; i <= m; i++) {
scanf("%d", &s[i].power);
s[i].id = i;
q.push(node(s[i].id, s[i].power));
}
int ans1 = 0, ans2 = 0, pos = n;
while (!q.empty() && pos >= 1) {
node t = q.top();
q.pop();
if (t.power > p[pos].power) {
if (t.power != 1) {
t.power = (t.power + 1) / 2;
cnt[t.id]++;
q.push(t);
}
} else {
if (t.power == p[pos].power) {
ans1++;
ans2 += cnt[t.id];
match[p[pos].id][0] = t.id;
match[t.id][1] = 1;
} else q.push(t);
pos--;
}
}
printf("%d %d\n", ans1, ans2);
for (int i = 1; i <= m; i++)
printf("%d%c", match[i][1] == 0 ? 0 : cnt[i], " \n"[i == m]);
for (int i = 1; i <= n; i++)
printf("%d%c", match[i][0], " \n"[i == n]);
}
#ifdef LOCAL_NORTH
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // LOCAL_NORTH
return 0;
}
F. Tourist Reform
给出一个n个点,m条边的连通无重边的无向图。现在把无向变有向,使得r[i](r[i]表示从i出发能够达到的点的个数,包含到达自身)的最小值最大。
首先可以知道的是,限制变换后的有向图的the minimum of r[i]的大小的一定是原无向图中的桥。因为去掉原图中的桥的时候,原图会被分为两个不连通的子图,所以在变换后,必定会有一个子图无法到达另一个子图。所以瓶颈是原图中的桥。进而就可以得出:变换后的有向图中的桥都必须背向顶点数最多的双连通分量。这样操作就会使只有一个双连通分量被孤立,同时被孤立的点的数目是最多的。
然后考虑把原图中的桥断开后,原图就变成了若干个双连通分量,下一步就是把它们变为强连通分量,使得连通分量内部两两可达。
所以具体过程就是:
(1)求原图中的桥
(2)去掉图中的桥,把双连通分量变为强连通分量。每次从双连通分量中的任意一点出发开始dfs,每走一条边就记录答案,通过vis来判断是否已经走过该点,同时不能走桥。每进行完一次dfs,就更新一下顶点数最多的双连通分量,start为该连通分量中的任意一点。
(3)从start出发开始dfs,遇到桥就记录一下答案,注意要反向记录。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
typedef __int64 LL;
typedef pair<int, int> PII;
#define mp make_pair
#define pb push_back
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
#define lowbit(x) ((x)&(-(x)))
#define bitcnt(x) __builtin_popcount(x)
#define bitcntll(x) __builtin_popcountll(x)
#define debug puts("-------------");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int MAXN = 4e5 + 50;
const int MAXM = 4e5 + 50;
int n, m, ans[MAXN][2];
struct Edge {
int from, to, nxt, d;
bool cut;
} E[MAXM << 1];
int Head[MAXN], tot;
int Low[MAXN], DFN[MAXN], Stack[MAXN];
int Index, top;
bool Instack[MAXN];
bool cut[MAXN];
int add_block[MAXN];
int bridge;
void edge_add(int u, int v) {
E[tot].from = u; E[tot].to = v; E[tot].nxt = Head[u]; E[tot].d = 0; E[tot].cut = false; Head[u] = tot++;
E[tot].from = v; E[tot].to = u; E[tot].nxt = Head[v]; E[tot].d = 0; E[tot].cut = false; Head[v] = tot++;
}
void Tarjan(int u, int pre) {
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
int son = 0;
for(int i = Head[u]; i != -1; i = E[i].nxt) {
v = E[i].to;
if(v == pre)continue;
if( !DFN[v] ) {
son++;
Tarjan(v, u);
if(Low[u] > Low[v])Low[u] = Low[v];
if(Low[v] > DFN[u]) {
bridge++;
E[i].cut = true;
E[i ^ 1].cut = true;
}
if(u != pre && Low[v] >= DFN[u]) {
cut[u] = true;
add_block[u]++;
}
} else if( Low[u] > DFN[v])
Low[u] = DFN[v];
}
if(u == pre && son > 1)cut[u] = true;
if(u == pre)add_block[u] = son - 1;
Instack[u] = false;
top--;
}
bool vis[MAXN];
int dcc;
void dfs1(int u, int pre) {
dcc++;
vis[u] = true;
for (int i = Head[u]; ~i; i = E[i].nxt) {
int v = E[i].to;
if (E[i].cut) continue;
ans[i >> 1][0] = u;
ans[i >> 1][1] = v;
if (!vis[v]) dfs1(v, u);
}
}
void dfs2(int u) {
vis[u] = true;
for (int i = Head[u]; ~i; i = E[i].nxt) {
int v = E[i].to;
if (!vis[v]) {
if (E[i].cut) {
ans[i >> 1][0] = v;
ans[i >> 1][1] = u;
}
dfs2(v);
}
}
}
void init() {
memset(DFN, 0, sizeof(DFN));
memset(Instack, false, sizeof(Instack));
memset(add_block, 0, sizeof(add_block));
memset(cut, false, sizeof(cut));
Index = top = 0;
bridge = 0;
tot = 0;
memset(Head, -1, sizeof(Head));
memset(vis, false, sizeof(vis));
}
int main() {
#ifdef LOCAL_NORTH
FIN;
#endif // LOCAL_NORTH
while (~scanf("%d%d", &n, &m)) {
init();
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v);
edge_add(u, v);
}
for (int i = 1; i <= n; i++)
if (!DFN[i])
Tarjan(i, i);
int start, maxx = 0;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
dcc = 0;
dfs1(i, i);
if (dcc > maxx) {
maxx = dcc;
start = i;
}
}
memset(vis, false, sizeof(vis));
dfs2(start);
printf("%d\n", maxx);
for (int i = 0; i < m; i++) {
printf("%d %d\n", ans[i][0], ans[i][1]);
}
}
#ifdef LOCAL_NORTH
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // LOCAL_NORTH
return 0;
}