大一上第四周学习笔记

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;	
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值