A
主席树…先放着吧
B
单源最短路+贪心。
(1)先跑一遍dij,然后再从1开始bfs,如果在最短路的边中就把它加进来,直到为k。(要注意的是dij的bfs和再次遍历的bfs是不一样的,前者的vis是看是否已经是最短路,后者的vis是看是否已经遍历过。)
(2)最短路树+贪心选边。
最短路树:定义构建一棵树,使得任意不属于根的节点x,dis(root,x)=原图走到x的最短路。
构建方法:就是在跑dijkstra时同时维护每个点是哪个点哪条边更新的,这个点这条边就是它在最短路树上的父亲/到父亲的边
dij跑一遍肯定是会得到一颗最短路树的,然后从1开始选边,每选一条边也一定会有一个点被加进来。所以贪心选边就行。(注意选满和不选满的情况)
两个方法也没差啦…
C
二分。
当前操作不定可能与之后有关+判断比查找容易:就用二分
D
数论dp,利用余数逐步推进…长见识了
其实就是从0开始一直遍历到结果为止。只存有用的数值(余数和位数和)。如果余数相同位数也相同,那之后加上一位的情况也是相同的,如果位数和已经大于s了就也不要再加位了(剪枝)
神奇=w=
#include<iostream>
#include<stdio.h>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
int vis[505][5005];
int d, s;
struct node {
int mod;
int bit;
string s;
};
string bfs() {
queue<node> q;
vis[0][0] = 1;
node temp;
temp.mod = 0;
temp.bit = 0;
temp.s = "";
q.push(temp);
while (!q.empty()) {
node now, next;
now = q.front();
q.pop();
//cout << now.s << "\n";
if (now.mod == 0 && now.bit == s) {
return now.s;
}
if (now.bit > s)continue;//剪枝*1
for (int i = 0; i <= 9; i++) {
next.mod = (now.mod * 10 + i) % d;
next.bit = now.bit + i;
if (!vis[next.mod][next.bit]) {//剪枝*2
vis[next.mod][next.bit] = 1;
next.s = now.s + (char)('0' + i);
q.push(next);
}
}
}
return "-1";
}
int main()
{
memset(vis, 0, sizeof(vis));
scanf("%d%d", &d, &s);
cout << bfs();
}
E
思维题。
要注意的是,mod操作不要太多,不然会T。
for (int i = 0; i < n; i++) {
ans += (a[i] * b[i] * w) % mod;
ans %= mod;
w <<= 1;
w %= mod;
}
权值mod之后也不要紧,之前以为不行就每到一轮重新计算权值…就t了
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
结合律 :((a+b) % p + c) % p = (a + (b+c) % p) % p
((a*b) % p * c)% p = (a * (b*c) % p) % p
分配律 :((a+b)%p*c)%p = ((a*c)%p + (b*c)%p)%p
a ^ b % p = ((a % p)^b) % p
重要定理
若a≡b (% p),则对于任意的c,都有(a + c) ≡ (b + c) (%p);
若a≡b (% p),则对于任意的c,都有(a * c) ≡ (b * c) (%p);
若a≡b (% p),c≡d (% p),
(a + c) ≡ (b + d) (%p),
(a - c) ≡ (b - d) (%p),
(a * c) ≡ (b * d) (%p),
(a / c) ≡ (b / d) (%p);
F
控制向左走和向右走的步数,不控制向上与向下的步数
我一开始想的是dfs搜索,但是这样要考虑很多情况,看了别人都才知道可以先预处理最短路然后判断…
思路:要是能得到向左走的步数的最小值,那么向右的步数(j-c+dis[i][j])也可以得到。
要得到就bfs最短路。
PS:因为是0-1路所以可以用deque来维护(就是优先队列啦,只不过01就两个值所以easy)
算法正确性明显可得…但是我就是想不到Q-Q哎
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<queue>
using namespace std;
int n, m, r, c, x, y;
char map[2005][2005];
int dis[2005][2005];
int dir[4][2] = { 0,-1,0,1,1,0,-1,0 };
struct node {
int x, y;
};
void bfs(int x, int y) {
memset(dis, 1, sizeof(dis));
deque<node> q;
node now, next;
dis[x][y] = 0;
now.x = x;
now.y = y;
q.push_front(now);
while (!q.empty()) {
now = q.front();
q.pop_front();
for (int i = 0; i < 4; i++) {//0 right
int tx = now.x + dir[i][0];
int ty = now.y + dir[i][1];
if (tx > 0 && tx <= n && ty > 0 && ty <= m && map[tx][ty] == '.') {//只用看一个变量,就是向左走的值,所以其他方向不改变值
if (dis[tx][ty] > dis[now.x][now.y] + (i == 0)) {
dis[tx][ty] = dis[now.x][now.y] + (i == 0);
next.x = tx;
next.y = ty;
if (i == 0)q.push_back(next);
else q.push_front(next);
}
}
}
}
}
int main()
{
freopen("in.txt", "r", stdin);
scanf("%d%d%d%d%d%d", &n, &m, &r, &c, &x, &y);
for (int i = 1; i <= n; i++) {
getchar();
for (int j = 1; j <= m; j++) {
scanf("%c", &map[i][j]);
}
}
bfs(r, c);
int cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (map[i][j] == '*')continue;
if (dis[i][j] <= x && dis[i][j]+j-c <= y) {
cnt++;
}
}
}
printf("%d\n", cnt);
}