10.5 周一
国庆浪了好久
其实浪完了我真的不知道要干啥了
这种生活其实是很空虚的
我以前以为算法竞赛占据了我太多时间,没有时间享受其他事情
其实这说明我还不热爱它
这是编程这件事使我的生活变得充实
这也是我感兴趣,有天赋,有前景的东西
为什么不全力以赴呢
找回对编程的热爱,而不是为了保研
想起了我以前看得《驱动力》这本书
现实的利益这样外在驱动力其实是不长久,不稳定的。
真正强大,稳定的驱动力是内在驱动力。
对于我,就是热爱,享受解题的乐趣
我不会花这么多时间在我不热爱的事情
我编程,只因为我爱它
大学找到自己热爱的事情并为之奋斗,是多快乐的事情
接下来三天开启集训模式,自我集训。上午3小时,下午晚上一起3小时。
洛谷 P1080 国王游戏
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e4 + 10;
struct node
{
int l, r;
}a[MAXN];
bool cmp(node x, node y)
{
return x.r * x.l < y.r * y.l;
}
struct bignum
{
int s[MAXN], len;
bignum() { memset(s, 0, sizeof(s)); len = 0; } //初始化
};
bignum ma(bignum a, bignum b)
{
if(a.len > b.len) return a;
if(a.len < b.len) return b;
for(int i = a.len; i >= 1; i--)
{
if(a.s[i] > b.s[i]) return a;
if(a.s[i] < b.s[i]) return b;
}
return a;
}
bignum operator * (bignum a, int b) //注意这个先乘再进位再位数。不要从0开始弄位数,因为可能中间有0
{
int& len = a.len;
_for(i, 1, len) a.s[i] *= b;
_for(i, 1, len) a.s[i+1] += a.s[i] / 10, a.s[i] %= 10;
while(a.s[len+1])
{
len++; //先加再处理进位
a.s[len+1] += a.s[len] / 10;
a.s[len] %= 10;
}
return a;
}
bignum operator / (bignum a, int b) //这种重载运算符的写法非常的爽。同时复习高精度除以低精度,其实不难,自己写个竖式模拟一下就好了
{
int yu = 0;
bignum res; res.len = a.len;
for(int i = a.len; i >= 1; i--)
{
int t = yu * 10 + a.s[i]; //把握好余数
res.s[i] = t / b;
yu = t % b;
}
while(!res.s[res.len] && res.len > 0) res.len--;
return res;
}
bignum init(int x) //int初始化成bignum
{
bignum res;
int& len = res.len; //&主要是偷懒,不用写那么长
while(x)
{
len++;
res.s[len] = x % 10;
x /= 10;
}
return res;
}
void print(bignum x) //输出
{
for(int i = x.len; i >= 1; i--) printf("%d", x.s[i]);
}
int main()
{
int n, x, y;
scanf("%d%d%d", &n, &x, &y);
REP(i, 0, n) scanf("%d%d", &a[i].l, &a[i].r);
sort(a, a + n, cmp);
bignum ans = init(0), sum = init(x);
REP(i, 0, n)
{
ans = ma(ans, sum / a[i].r);
sum = sum * a[i].l;
}
print(ans);
return 0;
}
这题的价值太大了,真的非常锻炼我。独立做出,非常爽。这样的难度刚好
这题有两个难点,一个是如何贪心,一个是如何高精度
贪心方面可以拿两个大臣谁前谁后做个比较来得出
高精度的话,这道题高精度搞懂,以后遇到题目里面有高精度就完全不怂了
其实我大部分时间都在写高精度。以后遇到写个bignum就好
诚实说绿题的难度比较适合我。也就是提高的难度比我水平高一点。多做绿题以及其之前的题
10.6 周二
每天训练结束都问自己有没有独立思考,有没有抄题解
洛谷P2392 kkksc03考前临时抱佛脚
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
int ans, s[10], a[30], sum, res;
void dfs(int x, int s, int n)
{
if(s > sum / 2) return;
res = max(res, s);
if(x == n + 1) return;
dfs(x + 1, s + a[x], n);
dfs(x + 1, s, n);
}
int f(int n)
{
sum = res = 0;
_for(i, 1, n) sum += a[i];
dfs(1, 0, n);
return sum - res;
}
int main()
{
scanf("%d%d%d%d", &s[1], &s[2], &s[3], &s[4]);
_for(i, 1, 4)
{
_for(j, 1, s[i]) scanf("%d", &a[j]);
ans += f(s[i]);
//printf("## %d\n", f(s[i]));
}
printf("%d\n", ans);
return 0;
}
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
int ans, s[10], a[30], sum, res;
int f(int n)
{
sum = res = 0;
_for(i, 1, n) sum += a[i];
REP(k, 0, pow(2, n))
{
int t = k, now = 0, p = 0;
while(t)
{
p++;
if(t & 1) now += a[p];
t >>= 1;
}
//printf("## now: %d\n", now);
if(now <= sum / 2) res = max(res, now);
}
return sum - res;
}
int main()
{
scanf("%d%d%d%d", &s[1], &s[2], &s[3], &s[4]);
_for(i, 1, 4)
{
_for(j, 1, s[i]) scanf("%d", &a[j]);
ans += f(s[i]);
}
printf("%d\n", ans);
return 0;
}
很容易想到最接近一半就行
数组范围小,直接爆搜
后来我试了一下用二进制枚举子集,也可以过,但时间复杂度要多乘一个n
这道题还可以用01背包,不过太久我已经忘记,以后会复习动态规划的
洛谷 P1443 马的遍历
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 500;
struct node{ int x, y; };
int Map[MAXN][MAXN], n, m, sx, sy;
int dir[8][2] = {1, 2, 1, -2, -1, 2, -1, -2, 2, 1, 2, -1, -2, 1, -2, -1};
void bfs()
{
memset(Map, -1, sizeof(Map));
Map[sx][sy] = 0;
queue<node> q;
q.push(node{sx, sy});
while(!q.empty())
{
node u = q.front(); q.pop();
REP(i, 0, 8)
{
int x = u.x + dir[i][0], y = u.y + dir[i][1];
if(Map[x][y] != -1 || x < 1 || x > n || y < 1 || y > m) continue;
Map[x][y] = Map[u.x][u.y] + 1;
q.push(node{x, y});
}
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &sx, &sy);
bfs();
_for(i, 1, n)
{
_for(j, 1, m)
printf("%-5d", Map[i][j]);
puts("");
}
return 0;
}
复习bfs
洛谷 P1135 奇怪的电梯
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 250;
int step[MAXN], k[MAXN], n, a, b, s[2] = {1, -1};
void bfs()
{
memset(step, -1, sizeof(step));
step[a] = 0;
queue<int> q;
q.push(a);
while(!q.empty())
{
int u = q.front(); q.pop();
REP(i, 0, 2)
{
int v = u + k[u] * s[i];
if(v < 1 || v > n || step[v] != -1) continue;
step[v] = step[u] + 1;
if(v == b) return;
q.push(v);
}
}
}
int main()
{
scanf("%d%d%d", &n, &a, &b);
_for(i, 1, n) scanf("%d", &k[i]);
bfs();
printf("%d\n", step[b]);
return 0;
}
bfs可以用来解决像这样a到b最短需要多少步的题目。这是bfs的优势所在,因为第一个搜到的一定是最短的
复习的速度比我想的要快
争取在寒假前把自己以前比较熟悉的部分全部掌握,加油
复习一二维前缀和差分
这个博客写的很好前缀和与差分
一维前缀和
支持O(1)查询[l,r]的和
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 100;
int a[MAXN], n, s[MAXN];
int main()
{
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%d", &a[i]);
s[i] = s[i-1] + a[i];
}
while(1)
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l-1]);
}
return 0;
}
一维差分
支持O(1)使[l,r]上加上一个p,最后输出每一个元素
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 100;
int a[MAXN], n, d[MAXN];
int main()
{
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%d", &a[i]);
d[i] = a[i] - a[i-1];
}
while(1)
{
int l, r, p;
scanf("%d%d%d", &l, &r, &p);
if(l == 0 && r == 0) break;
d[l] += p; d[r+1] -= p;
}
_for(i, 1, n) d[i] += d[i-1];
_for(i, 1, n) printf("%d ", d[i]);
return 0;
}
二维前缀和
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 100;
int a[MAXN][MAXN], s[MAXN][MAXN], n, m;
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, n)
_for(j, 1, m)
{
scanf("%d", &a[i][j]);
s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];
}
while(1)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]);
}
return 0;
}
二维差分
d[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1]
洛谷 P3397 地毯
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e3 + 10;
int d[MAXN][MAXN], n, m;
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
d[x1][y1]++;
d[x1][y2+1]--;
d[x2+1][y1]--;
d[x2+1][y2+1]++;
}
_for(i, 1, n)
{
_for(j, 1, n)
{
d[i][j] += d[i-1][j] + d[i][j-1] - d[i-1][j-1];
printf("%d ", d[i][j]);
}
puts("");
}
return 0;
}
洛谷 P1551 亲戚
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 5000 + 10;
int f[MAXN], n, m, p;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
void Union(int x, int y) { f[find(x)] = find(y); }
int main()
{
scanf("%d%d%d", &n, &m, &p);
_for(i, 1, n) f[i] = i;
while(m--)
{
int x, y;
scanf("%d%d", &x, &y);
Union(x, y);
}
while(p--)
{
int x, y;
scanf("%d%d", &x, &y);
if(find(x) == find(y)) puts("Yes");
else puts("No");
}
return 0;
}
复习并查集。并查集代码好短,也很好理解,用来做集合的问题
洛谷 P1102 A-B 数对
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 2e5 + 10;
int a[MAXN], n, c;
map<int, int> s;
int main()
{
scanf("%d%d", &n, &c);
_for(i, 1, n)
{
scanf("%d", &a[i]);
s[a[i]+c]++;
}
long long ans = 0;
_for(i, 1, n) ans += s[a[i]];
printf("%lld\n", ans);
return 0;
}
复习了map,一次操作复杂度logn
大致规划
整个10月,接下来复习树状数组线段树,图论,数学,杂项
11月复习动态规划
12月1月大量刷题,刷绿题
10.7 周三
洛谷 P3374 【模板】树状数组 1
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 5e5 + 10;
int c[MAXN], n, m;
int lowbit(int x) { return x & (-x); }
void add(int x, int p)
{
while(x <= n)
{
c[x] += p;
x += lowbit(x);
}
}
int s(int x)
{
int res = 0;
while(x)
{
res += c[x];
x -= lowbit(x);
}
return res;
}
int sum(int x, int y) { return s(y) - s(x-1); }
int main()
{
int k, x, y;
scanf("%d%d", &n, &m);
_for(i, 1, n)
{
scanf("%d", &x);
add(i, x);
}
while(m--)
{
scanf("%d%d%d", &k, &x, &y);
if(k == 1) add(x, y);
else printf("%d\n", sum(x, y));
}
return 0;
}
复习树状数组。单点修改,区间查询
洛谷 P3368 【模板】树状数组 2
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 5e5 + 10;
int c[MAXN], n, m;
int lowbit(int x) { return x & (-x); }
void add(int x, int p)
{
while(x <= n)
{
c[x] += p;
x += lowbit(x);
}
}
int sum(int x)
{
int res = 0;
while(x)
{
res += c[x];
x -= lowbit(x);
}
return res;
}
int main()
{
int k, x, y, t = 0;
scanf("%d%d", &n, &m);
_for(i, 1, n)
{
scanf("%d", &x);
add(i, x - t);
t = x;
}
while(m--)
{
scanf("%d", &k);
if(k == 1)
{
scanf("%d%d%d", &x, &y, &t);
add(x, t); add(y + 1, -t);
}
else
{
scanf("%d", &x);
printf("%d\n", sum(x));
}
}
return 0;
}
区间修改,单点查询。用差分转化一下就好了
二维树状数组也挺简单,也是支持单点修改区间查询,区间修改,单点查询
#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e4;
int f[MAXN][MAXN], n, m, p;
inline int lowbit(int x) { return x & (-x); }
inline void add(int x, int y, int q)
{
while(x <= n)
{
for(int i = y; i <= m; i += lowbit(i)) //注意for循环的写法
f[x][i] += q;
x += lowbit(x);
}
}
inline int sum(int x, int y)
{
int res = 0;
while(x)
{
for(int i = y; i; i -= lowbit(i))
res += f[x][i];
x -= lowbit(x);
}
return res;
}
inline int ans(int x1, int y1, int x2, int y2)
{
return sum(x1, y1) + sum(x2-1, y2-1) - sum(x1, y2 - 1) - sum(x2 - 1, y1);
}
int main()
{
scanf("%d%d%d", &n, &m, &p);
_for(i, 1, n)
_for(j, 1, m)
{
int x; scanf("%d", &x);
add(i, j, x);
}
while(p--)
{
int k, x, y, q, a, b;
scanf("%d%d%d", &k, &x, &y);
if(k == 1) scanf("%d", &q), add(x, y, q);
else
{
scanf("%d%d", &a, &b);
printf("%d\n", ans(a, b, x, y));
}
}
return 0;
}
洛谷 P3397 地毯
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e3 + 10;
int c[MAXN][MAXN], n, m, k;
int lowbit(int x) { return x & (-x); }
int add(int x, int y, int p)
{
while(x <= n)
{
for(int i = y; i <= m; i += lowbit(i))
c[x][i] += p;
x += lowbit(x);
}
}
int sum(int x, int y)
{
int res = 0;
while(x)
{
for(int i = y; i; i -= lowbit(i))
res += c[x][i];
x -= lowbit(x);
}
return res;
}
void updata(int x1, int y1, int x2, int y2)
{
add(x1, y1, 1); add(x1, y2+1, -1); add(x2+1, y1, -1); add(x2+1, y2+1, 1);
}
int main()
{
scanf("%d%d", &n, &k); m = n;
while(k--)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
updata(x1, y1, x2, y2);
}
_for(i, 1, n)
{
_for(j, 1, n)
printf("%d ", sum(i, j));
puts("");
}
return 0;
}
洛谷 P1908 逆序对
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int MAXN = 5e5 + 10;
int c[MAXN], a[MAXN], b[MAXN], n;
void read(int& x)
{
int sign = 1; x = 0; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') sign = -1; ch = getchar(); }
while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
x *= sign;
}
int lowbit(int x) { return x & -x; }
void add(int x, int p)
{
while(x <= MAXN)
{
c[x] += p;
x += lowbit(x);
}
}
int sum(int x)
{
int res = 0;
while(x)
{
res += c[x];
x -= lowbit(x);
}
return res;
}
int main()
{
scanf("%d", &n);
REP(i, 0, n) read(b[i]), a[i] = b[i];
sort(b, b + n);
int t = unique(b, b + n) - b;
REP(i, 0, n) a[i] = lower_bound(b, b + t, a[i]) - b + 1;
ll ans = 0;
REP(i, 0, n)
{
ans += i - sum(a[i]);
add(a[i], 1);
}
printf("%lld\n", ans);
return 0;
}
独立做出,爽!
学到了蛮多
一 是复习了读入优化
二 我做道题时很快就想到了树状数组的解法。但是1e9空间显然会炸。于是我用了map,因为map一次操作是logn,所以超时了50分
后来我就想到了离散化,因为只有大小关系有价值,绝对大小没有价值
离散化有两种方法,我用的是存在另外一个数组,排序去重,然后用原来的数组二分找出。
还可以用结构体的方法,但是结构体的离散化不能去重,也就是说如果有重复的元素,绝对大小相同的会赋予不同的值。这种情况需要额外处理。
洛谷 P3372 【模板】线段树 1
#include<bits/stdc++.h>
#define l(k) (k << 1)
#define r(k) ((k << 1) + 1)
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
struct node
{
ll f, w;
int l, r;
int len() { return r - l + 1; }
int m() { return (l + r) >> 1; }
}t[MAXN << 2];
void lazy(int k, ll p) { t[k].f += p; t[k].w += t[k].len() * p; }
void up(int k) { t[k].w = t[l(k)].w + t[r(k)].w; }
void down(int k)
{
lazy(l(k), t[k].f);
lazy(r(k), t[k].f);
t[k].f = 0;
}
void build(int k, int l, int r)
{
t[k].l = l; t[k].r = r; t[k].f = 0;
if(l == r)
{
scanf("%lld", &t[k].w);
return;
}
int m = t[k].m();
build(l(k), l, m);
build(r(k), m + 1, r);
up(k);
}
void add(int k, int x, ll p)
{
if(t[k].len() == 1)
{
t[k].w += p;
return;
}
if(t[k].f) down(k);
int m = t[k].m();
if(x <= m) add(l(k), x, p);
else add(r(k), x, p);
up(k);
}
void change(int k, int L, int R, ll p)
{
if(L <= t[k].l && t[k].r <= R)
{
lazy(k, p);
return;
}
if(t[k].f) down(k);
int m = t[k].m();
if(L <= m) change(l(k), L, R, p);
if(R > m) change(r(k), L, R, p);
up(k);
}
ll sum(int k, int L, int R)
{
if(L <= t[k].l && t[k].r <= R) return t[k].w;
if(t[k].f) down(k);
ll res = 0;
int m = t[k].m();
if(L <= m) res += sum(l(k), L, R);
if(R > m) res += sum(r(k), L, R);
return res;
}
int main()
{
int n, m, q, x, y, k;
scanf("%d%d", &n, &m);
build(1, 1, n);
while(m--)
{
scanf("%d%d%d", &q, &x, &y);
if(q == 1)
{
scanf("%d", &k);
change(1, x, y, k);
}
else printf("%lld\n", sum(1, x, y));
}
return 0;
}
线段树复习。这个代码量挺大的。今明两天每次做线段树题都从头开始打代码。
10.9 周五
加油,今天把线段树复习完
每道难题至少想两三个小时,在想的过程中进步
线段树每个数乘一个x的操作想半天想不出
停一下,复习一下快速幂
理解本质,其实就是b化为二进制而已
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
int cal(int a, int b, int p)
{
int res = 1 % p; a %= p;
while(b)
{
if(b & 1) res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
int main()
{
int a, b, p;
scanf("%d%d%d", &a, &b, &p);
printf("%d\n", cal(a, b, p));
return 0;
}
复习Floyd模板。可以用来找最小权值环和判断连通性
_for(i, 1, n)
_for(j, 1, n)
f[i][j] = (i == j ? 0 : 1e9)
_for(k, 1, n)
_for(i, 1, n)
_for(j, 1, n)
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
复习堆优化dijstra。复杂度mlogn 只能用于没有负权边的情况
#include<bits/stdc++.h>
#define pb push_back
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e4 + 10;
struct node
{
int v, w;
bool operator < (const node& rhs) const
{
return w > rhs.w;
}
};
vector<node> g[MAXN];
int d[MAXN], n, m, s;
void work()
{
_for(i, 1, n) d[i] = 1e9; d[s] = 0;
priority_queue<node> q;
q.push(node{s, 0});
while(!q.empty())
{
node x = q.top(); q.pop();
int u = x.v;
if(d[u] != x.w) continue;
REP(i, 0, g[u].size())
{
int v = g[u][i].v, w = g[u][i].w;
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
q.push(node{v, d[v]});
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &s);
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].pb(node{v, w});
}
work();
_for(i, 1, n)
{
if(d[i] == 1e9) d[i] = (1 << 31) - 1;
printf("%d ", d[i]);
}
return 0;
}
spfa,有负权边的情况
#include<bits/stdc++.h>
#define pb push_back
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e4 + 10;
struct node { int v, w; };
vector<node> g[MAXN];
int d[MAXN], vis[MAXN], n, m, s;
void work()
{
_for(i, 1, n) d[i] = 1e9; d[s] = 0;
queue<int> q;
q.push(s); vis[s] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 0;
REP(i, 0, g[u].size())
{
int v = g[u][i].v, w = g[u][i].w;
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &s);
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].pb(node{v, w});
}
work();
_for(i, 1, n)
{
if(d[i] == 1e9) d[i] = (1 << 31) - 1;
printf("%d ", d[i]);
}
return 0;
}
洛谷 P1629 邮递员送信
卡了好久
其实很快就想到了建反图
但是想不清楚觉得没用
后来我在纸上随便画了一下发现画反图就是答案
所以一定一定要在草稿纸上画图,把脑子里的想法形象化,清晰化
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e3 + 10;
struct node
{
int v, w;
bool operator < (const node& rhs) const
{
return w > rhs.w;
}
};
vector<node> g[MAXN], k[MAXN];
int d[MAXN], n, m;
void work1()
{
_for(i, 1, n) d[i] = 1e9; d[1] = 0;
priority_queue<node> q;
q.push({1, 0});
while(!q.empty())
{
node x = q.top(); q.pop();
int u = x.v;
if(d[u] != x.w) continue;
REP(i, 0, g[u].size())
{
int v = g[u][i].v, w = g[u][i].w;
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
q.push(node{v, d[v]});
}
}
}
}
void work2()
{
_for(i, 1, n) d[i] = 1e9; d[1] = 0;
priority_queue<node> q;
q.push({1, 0});
while(!q.empty())
{
node x = q.top(); q.pop();
int u = x.v;
if(d[u] != x.w) continue;
REP(i, 0, k[u].size())
{
int v = k[u][i].v, w = k[u][i].w;
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
q.push(node{v, d[v]});
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back(node{v, w});
k[v].push_back(node{u, w});
}
int ans = 0;
work1(); _for(i, 2, n) ans += d[i];
work2(); _for(i, 2, n) ans += d[i];
printf("%d\n", ans);
return 0;
}
10.10 周六
今天怒干支持每一个数支持乘法的线段树,灵光一闪有了突破的进展,但交上去只有30分,但比0分好多了
然后实在不知错哪里,就放一放
复习了一下最小生成树
生成树就是用n-1条边联通
Kruskal其实很简单
贪心,边权从小到大连,能连就连
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= b; i++)
using namespace std;
const int MAXN = 5e3 + 10;
struct node
{
int u, v, w;
bool operator < (const node& rhs) const
{
return w < rhs.w;
}
};
vector<node> Edge;
int f[MAXN], n, m;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
Edge.push_back(node{u, v, w});
}
_for(i, 1, n) f[i] = i;
sort(Edge.begin(), Edge.end());
int ans = 0, cnt = 0;
REP(i, 0, Edge.size())
{
int u = find(Edge[i].u), v = find(Edge[i].v);
if(u != v)
{
f[u] = v;
ans += Edge[i].w;
cnt++;
}
}
if(cnt != n - 1) puts("orz");
else printf("%d\n", ans);
return 0;
}
明天刚线段树那道题以及最小生成树的一些题目
加油
10.11 周日
洛谷 p1194 买礼物
为何要用最小生成树呢
贪心加边,加完为止,这个过程就是最小生成树
我写完交上去90分
后来想到会不会kij还大于a,然后建边处理了一下,就ac了
其实这组数据加的毫无必要,因为这组数据不符合题意,题目说了很清楚是优惠,就不可能存在这种情况
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e3 + 10;
struct node
{
int u, v, w;
bool operator < (const node& rhs) const
{
return w < rhs.w;
}
};
vector<node> Edge;
int f[MAXN], a, n, cnt, ans;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
scanf("%d%d", &a, &n);
_for(i, 1, n) f[i] = i;
_for(i, 1, n)
_for(j, 1, n)
{
int w; scanf("%d", &w);
if(w != 0 && j > i && w <= a) Edge.push_back(node{i, j, w});
}
cnt = n;
sort(Edge.begin(), Edge.end());
REP(i, 0, Edge.size())
{
int u = find(Edge[i].u), v = find(Edge[i].v);
if(u != v)
{
f[u] = v;
ans += Edge[i].w;
cnt--;
}
}
ans += cnt * a;
printf("%d\n", ans);
return 0;
}
p1991 无线通讯网
二分+最小生成树
审题时看到最大值最小或最小值最大要想到二分答案
注意逆向思考,有时反过来想就对了
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1e3 + 10;
struct node
{
int u, v;
double w;
bool operator < (const node& rhs) const
{
return w < rhs.w;
}
};
vector<node> Edge, ans;
int f[MAXN], vis[MAXN], k, n;
double x[MAXN], y[MAXN];
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
double dist(int a, int b)
{
return sqrt(pow(x[a]-x[b], 2) + pow(y[a]-y[b], 2));
}
bool check(double key)
{
_for(i, 1, n) f[i] = i;
memset(vis, 0, sizeof(vis));
int cnt = 0;
REP(i, 0, Edge.size())
{
int u = find(Edge[i].u), v = find(Edge[i].v);
if(u != v)
{
f[u] = v;
if(Edge[i].w > key)
{
cnt += !vis[u] + !vis[v];
vis[u] = vis[v] = 1;
}
}
}
return cnt <= k;
}
int main()
{
scanf("%d%d", &k, &n);
_for(i, 1, n) scanf("%lf%lf", &x[i], &y[i]);
_for(i, 1, n)
_for(j, i + 1, n)
Edge.push_back(node{i, j, dist(i, j)});
sort(Edge.begin(), Edge.end());
double l = 0, r = 2e4;
while(l + 1e-4 < r)
{
double mid = (l + r) / 2;
if(!check(mid)) l = mid;
else r = mid;
}
printf("%.2lf\n", r);
return 0;
}