OI-note
版权声明:仅供学习。
持续更新中...也算是个人学习的监督与激励吧。
OI路漫漫,且行且珍惜。
OI太颓了,模拟赛都打不动,班级全是大佬。
算法综合
杂题综合
Index 1:
While moving to a new compound the Byteotian Institute of Experimental Physics has encountered a logisticalproblem - the transfer of its vast collection of precision weights turned out to be non-trivial.
The Institute has a certain number of containers of limited strength at its disposal. As many weightsas possible are to be put into the containers, the remaining ones will be discarded. There is no limit onthe number of weights to be put in a container apart from the requirement of not exceeding its strength. Acontainer may also be empty.
Any two weights in the Institute have a peculiar property: the mass of one of them is an integer multipleof the mass of the other. Particularly, they may have the same mass.
TaskWrite a programme which:
reads the durabilities of the containers and masses of the weights from the standard input, determines the maximal number of weights that can be put in the containers, writes the outcome to the standard output.
The first line of the standard input contains two integers
The first and only line of the standard output should contain a single integer -the maximal number ofweights that can be placed in the containers without exceeding their durability.
Aim 1:题目大意
求最大能装下多少砝码。
较为特别的砝码的质量两两成倍数关系。
看起来是什么背包,但是。。。
所以我们要抓住砝码的质量两两成倍数关系。
这里显然暗藏玄机,其实很容易可以想到这和进制有关系,毕竟进制的每一位的权值都是倍数关系,如果没有这个砝码只是这一位为
Aim 2:砝码进制
此时我们有两个容器质量 13 9
这里有四个砝码 2 4 4 12
我们对容器进行砝码进制转化
每一位从小到大表示砝码质量,而最后剩下的数将对答案没有贡献。
同理
将两个砝码进制数相加,可得:
这样做的原因:我们知道它们成倍数关系,那么如果高位可以放下那么低位可以类似借位的形式向高位要。
剩下的部分一定小于任意砝码,不能被利用。
如果不懂可以多想想,不然您无法理解。
Aim 3:贪心借位
既然这样我们已近处理好了,我们发现无论做大的还是小的,其贡献均一致,那么显然!
先做质量小的。
此时我们做
用掉一个
此时遇到两个
此时我们得到了代码,在代码实现上注意借位的问题,即可解决,感谢观看!
代码经过格式化,用餐愉快。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m, cnt;
int a[N], b[N], c[N], d[N], num[N];
void dfs(int step, int now)
{
if (step == cnt + 1)
return;
if (c[step] && step == now)
return;
if (c[step])
{
c[step]--;
c[step - 1] += b[step] / b[step - 1];
return;
}
else
{
dfs(step + 1, now);
if (c[step] && step != now)
c[step]--, c[step - 1] += b[step] / b[step - 1];
}
}
signed main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
for (int i = 1; i <= m; ++i)
b[i] = read(), d[i] = b[i];
sort(d + 1, d + m + 1);
cnt = 0;
for (int i = 1; i <= m; ++i)
if (d[i] != d[i - 1])
b[++cnt] = d[i], num[cnt]++;
else
num[cnt]++;
for (int i = 1; i <= n; ++i)
{
for (int j = cnt; j >= 1; --j)
{
c[j] += a[i] / b[j];
a[i] %= b[j];
}
}
for (int i = 1, j = 1; i <= cnt; ++i)
{
while (num[i]--)
{
dfs(i, i);
if (c[i])
c[i]--;
else
{
printf("%d\n", j - 1);
return 0;
}
++j;
}
}
printf("%d\n", m);
return 0;
}
Index 2:
物流公司要把一批货物从码头 A 运到码头 B。由于货物量比较大,需要
物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。
但是修改路线是—件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个
第一行是四个整数
接下来
再接下来一行是一个整数
包括了一个整数表示最小的总成本。
总成本为
【数据范围】
对于
预处理出每一段时间内都走同一条路的最短路
于是我们可以很简单的得到前天的最小代价
于是这道题目就很巧妙的转换成DP。
即使我没想出来。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e2 + 50;
const int M = 1e3 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m, k, mm, d;
struct edge
{
int to, dis, nxt;
} e[M];
int head[N], cnt;
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
bool a[N][N];
bool b[N];
struct node
{
int pos;
int dis;
bool operator<(const node &x) const
{
return x.dis < dis;
}
};
priority_queue<node> q;
int dis[N];
bool vis[N];
void dij()
{
memset(dis, 0x3f3f3f3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[1] = 0;
q.push((node){1, 0});
while (!q.empty())
{
node tmp = q.top();
q.pop();
int x = tmp.pos;
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
if (b[y])
continue;
if (dis[y] > dis[x] + e[i].dis)
{
dis[y] = dis[x] + e[i].dis;
q.push((node){y, dis[y]});
}
}
}
}
int f[N], g[N][N];
signed main()
{
n = read(), m = read(), k = read(), mm = read();
for (int i = 1; i <= mm; ++i)
{
int u = read(), v = read(), w = read();
add_edge(u, v, w);
add_edge(v, u, w);
}
d = read();
for (int i = 1; i <= d; ++i)
{
int x = read(), u = read(), v = read();
for (int j = u; j <= v; ++j)
{
a[x][j] = 1;
}
}
for (int i = 1; i <= n; ++i)
{
for (int j = i; j <= n; ++j)
{
memset(b, 0, sizeof(b));
for (int u = i; u <= j; ++u)
{
for (int v = 1; v <= m; ++v)
{
if (a[v][u])
b[v] = 1;
}
}
dij();
g[i][j] = (dis[m] == 0x3f3f3f3f ? -1 : dis[m]);
}
}
memset(f, 0x3f3f3f3f, sizeof(f));
f[0] = 0;
for (int i = 1; i <= n; ++i)
{
if (g[1][i] != -1)
f[i] = g[1][i] * i;
for (int j = i; j >= 1; --j)
{
if (g[j][i] == -1)
continue;
f[i] = min(f[i], f[j - 1] + g[j][i] * (i - j + 1) + k);
}
}
printf("%d\n", f[n]);
return 0;
}
Index 3:
共有
某人去商店买东西,去了
输入的第一行是五个整数,分别代表
接下来
对于每次购买,输出一行一个整数代表答案。
- 对于
的数据,保证 , 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int c[5], n;
int d[5], s;
int f[N];
int g(int i) { return (d[i] + 1) * c[i]; }
signed main()
{
f[0] = 1;
for (int i = 1; i <= 4; ++i)
{
c[i] = read();
for (int j = c[i]; j <= N - 50; ++j)
{
f[j] += f[j - c[i]];
}
}
n = read();
while (n--)
{
for (int i = 1; i <= 4; ++i)
d[i] = read();
s = read();
int ans = f[s];
if (s >= g(1))
ans -= f[s - g(1)];
if (s >= g(2))
ans -= f[s - g(2)];
if (s >= g(3))
ans -= f[s - g(3)];
if (s >= g(4))
ans -= f[s - g(4)];
if (s >= g(1) + g(2))
ans += f[s - g(1) - g(2)];
if (s >= g(1) + g(3))
ans += f[s - g(1) - g(3)];
if (s >= g(1) + g(4))
ans += f[s - g(1) - g(4)];
if (s >= g(2) + g(3))
ans += f[s - g(2) - g(3)];
if (s >= g(2) + g(4))
ans += f[s - g(2) - g(4)];
if (s >= g(3) + g(4))
ans += f[s - g(3) - g(4)];
if (s >= g(1) + g(2) + g(3))
ans -= f[s - g(1) - g(2) - g(3)];
if (s >= g(1) + g(2) + g(4))
ans -= f[s - g(1) - g(2) - g(4)];
if (s >= g(1) + g(3) + g(4))
ans -= f[s - g(1) - g(3) - g(4)];
if (s >= g(2) + g(3) + g(4))
ans -= f[s - g(2) - g(3) - g(4)];
if (s >= g(1) + g(2) + g(3) + g(4))
ans += f[s - g(1) - g(2) - g(3) - g(4)];
printf("%lld\n", ans);
}
return 0;
}
Index 4:
从文件中读入一个正整数
例如,
只有一个正整数:
第
第
(提示:在给定的范围内,最大乘积的位数不超过
结论题,练习高精度可用
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int x[N];
int a[N];
int cnt = 0;
void dfs(int n)
{
while (n > 4)
{
n -= 3;
x[++cnt] = 3;
}
x[++cnt] = n;
}
int len;
void mul(int m)
{
int tmp = len, k = 0;
for (int i = 1; i <= tmp || k != 0; ++i)
{
len = max(len, i);
if (i <= tmp)
a[i] *= m;
a[i] += k;
k = a[i] / 10;
a[i] %= 10;
}
}
int main()
{
n = read();
dfs(n);
a[1] = 1;
++len;
for (int i = 1; i <= cnt; ++i)
{
mul(x[i]);
}
printf("%d\n", len);
for (int i = len; i >= max(len - 100 + 1, 1); --i)
{
printf("%d", a[i]);
}
printf("\n");
return 0;
}
Index 5:
写一个程序来模拟操作系统的进程调度。假设该系统只有一个 CPU,每一个进程的到达时间,执行时间和运行优先级都是已知的。其中运行优先级用自然数表示,数字越大,则优先级越高。
如果一个进程到达的时候 CPU 是空闲的,则它会一直占用 CPU 直到该进程结束。除非在这个过程中,有一个比它优先级高的进程要运行。在这种情况下,这个新的(优先级更高的)进程会占用 CPU,而老的只有等待。
如果一个进程到达时,CPU 正在处理一个比它优先级高或优先级相同的进程,则这个(新到达的)进程必须等待。
一旦 CPU 空闲,如果此时有进程在等待,则选择优先级最高的先运行。如果有多个优先级最高的进程,则选择到达时间最早的。
输入包含若干行,每一行有四个自然数(均不超过
按照进程结束的时间输出每个进程的进程号和结束时间。
模拟题+堆优化
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
struct que
{
int id, fir, len, vol;
bool operator<(const que &o) const
{
if (o.vol != vol)
return o.vol > vol;
else
return o.fir < fir;
}
} a;
priority_queue<que> q;
signed main()
{
int now = 0;
while (scanf("%lld", &a.id) != EOF)
{
scanf("%lld%lld%lld", &a.fir, &a.len, &a.vol);
while (!q.empty() && (now + q.top().len <= a.fir))
{
now += q.top().len;
printf("%lld %lld\n", q.top().id, now);
q.pop();
}
if (!q.empty())
{
que tmp = q.top();
q.pop();
tmp.len -= a.fir - now;
q.push(tmp);
}
q.push(a);
now = a.fir;
}
while (!q.empty())
{
que tmp = q.top();
q.pop();
now += tmp.len;
printf("%lld %lld\n", tmp.id, now);
}
return 0;
}
Index 6:
John 在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。
John 的每个农场有
现在 John 希望能够从某块地出发,走过一条路径回到出发点,且同时也回到了出发时刻以前的某一时刻。请你告诉他能否做到。
输入的第一行是一个整数
每组测试数据的格式如下:
每组数据的第一行是三个用空格隔开的整数,分别代表农田的个数
每组数据的第
每组数据的第
对于每组测试数据,输出一行一个字符串,如果能回到出发时刻之前,则输出 YES
,否则输出 NO
。
对于
SPFA 判负环经典题目
原理就是进队次数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int t, n, m, k;
struct edge
{
int to, dis, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int res[N], dis[N];
bool vis[N];
queue<int> q;
bool spfa()
{
for (int i = 1; i <= n; ++i)
{
q.push(i);
vis[i] = 1;
}
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = 0;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (dis[v] > dis[u] + e[i].dis)
{
dis[v] = dis[u] + e[i].dis;
res[v] = res[u] + 1;
if (res[v] >= n)
return 1;
if (!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
return 0;
}
int main()
{
t = read();
while (t--)
{
n = read(), m = read(), k = read();
memset(head, 0, sizeof(head));
memset(dis, 0x3f3f3f3f, sizeof(dis));
memset(res, 0, sizeof(res));
cnt = 0;
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read(), w = read();
add_edge(u, v, w);
add_edge(v, u, w);
}
for (int i = 1; i <= k; ++i)
{
int u = read(), v = read(), w = -read();
add_edge(u, v, w);
}
if (spfa())
{
puts("YES");
}
else
{
puts("NO");
}
}
return 0;
}
动态规划
开坑原因: 模拟赛被DP搞怕了。
况且本身DP就很颓,所以板刷DP。
绝不放弃任何一道DP!————RVG
DP综合
Index 1:
教主有着一个环形的花园,他想在花园周围均匀地种上n棵树,但是教主花园的土壤很特别,每个位置适合种的树都不一样,一些树可能会因为不适合这个位置的土壤而损失观赏价值。
教主最喜欢
第一行为一个正整数
接下来
第
一个正整数,为最大的观赏价值和。
对于
Aim 1:动态转移
注意因为是环形的所以,枚举第1棵树满足的取最大。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int a[N][3];
int f[N][3][2];
int main()
{
n = read();
for (int i = 1; i <= n; ++i)
a[i][0] = read(), a[i][1] = read(), a[i][2] = read();
int ans = 0;
for (int j = 0; j < 3; ++j)
{
memset(f, 0, sizeof(f));
f[1][j][0] = f[1][j][1] = a[1][j];
for (int i = 2; i <= n; ++i)
{
f[i][0][0] = max(f[i - 1][1][1], f[i - 1][2][1]) + a[i][0];
f[i][1][0] = f[i - 1][2][1] + a[i][1];
f[i][1][1] = f[i - 1][0][0] + a[i][1];
f[i][2][1] = max(f[i - 1][1][0], f[i - 1][0][0]) + a[i][2];
}
for (int i = 0; i < j; ++i)
ans = max(ans, f[n][i][0]);
for (int i = 2; i > j; --i)
ans = max(ans, f[n][i][1]);
}
printf("%d\n", ans);
return 0;
}
Index 2:
卡门――农夫约翰极其珍视的一条 Holsteins
奶牛――已经落了到 “垃圾井” 中。“垃圾井” 是农夫们扔垃圾的地方,它的深度为
卡门想把垃圾堆起来,等到堆得高度大等于于井的深度时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。
每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。
假设卡门预先知道了每个垃圾扔下的时间
第一行为两个整数,
第二到第
如果卡门可以爬出陷阱,输出一个整数,表示最早什么时候可以爬出;否则输出卡门最长可以存活多长时间。
Aim 1:动态转移
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e2 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
struct node
{
int t, f, h;
} a[N];
bool cmp(node x, node y)
{
return x.t < y.t;
}
int f[N][N];
int main()
{
m = read(), n = read();
int alive = 10;
for (int i = 1; i <= n; ++i)
{
a[i].t = read();
a[i].f = read();
a[i].h = read();
}
sort(a + 1, a + n + 1, cmp);
memset(f, -0x3f, sizeof(f));
f[0][0] = 10;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j <= m; ++j)
{
if (f[i - 1][j] >= a[i].t)
f[i][j] = max(f[i][j], f[i - 1][j] + a[i].f);
if (j >= a[i].h && f[i - 1][j - a[i].h] >= a[i].t)
f[i][j] = max(f[i][j], f[i - 1][j - a[i].h]);
}
}
bool flag = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j <= m; ++j)
{
if (f[i][j] >= a[i].t)
{
if (j == m)
{
printf("%d\n", a[i].t);
flag = 1;
break;
}
}
}
alive = max(alive, f[i][0]);
if (flag)
break;
}
if (!flag)
{
printf("%d\n", alive);
}
return 0;
}
Index 3:
一年一度的展会要来临了,Farmer John 想要把
John 非常的足智多谋,他计算出任何两只公牛之间至少要有
两个整数
输出约翰可以安排的方法数。考虑到这个数可能很大,你只要输出对
Aim 1:动态转移
表示当前位置放公牛的方案数
表示当前位置放奶牛的方案数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 5000011;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, k;
int fn[N], fg[N];
signed main()
{
n = read(), k = read();
fn[1] = fg[1] = 1;
for (int i = 2; i <= n; ++i)
{
fn[i] = fn[i - 1] + fg[i - 1];
fn[i] %= Mod;
if (i > k + 1)
fg[i] = fn[i - k - 1] + fg[i - k - 1];
else
fg[i] = 1;
fg[i] %= Mod;
}
printf("%lld\n", (fn[n] + fg[n]) % Mod);
return 0;
}
Index 4:
某人有一套玩具,并想法给玩具命名。首先他选择WING四个字母中的任意一个字母作为玩具的基本名字。然后他会根据自己的喜好,将名字中任意一个字母用“WING”中任意两个字母代替,使得自己的名字能够扩充得很长。
现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的。
第一行四个整数W、I、N、G。表示每一个字母能由几种两个字母所替代。
接下来W行,每行两个字母,表示W可以用这两个字母替代。
接下来I行,每行两个字母,表示I可以用这两个字母替代。
接下来N行,每行两个字母,表示N可以用这两个字母替代。
接下来G行,每行两个字母,表示G可以用这两个字母替代。
最后一行一个长度不超过Len的字符串。表示这个玩具的名字。
一行字符串,该名字可能由哪些字母变形而得到。(按照WING的顺序输出)
如果给的名字不能由任何一个字母变形而得到则输出“The name is wrong!”
表示 可以用 表示
表示 子段 是 转换的
根据这个思路区间DP即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
bool a[5][5][5];
bool f[250][250][5];
char opt[2];
int to(char x)
{
if (x == 'W')
return 1;
if (x == 'I')
return 2;
if (x == 'N')
return 3;
if (x == 'G')
return 4;
}
string s;
int m[5];
int main()
{
m[1] = read(), m[2] = read(), m[3] = read(), m[4] = read();
for (int i = 1; i <= 4; ++i)
{
for (int j = 1; j <= m[i]; ++j)
{
scanf("%s", opt);
a[i][to(opt[0])][to(opt[1])] = 1;
}
}
cin >> s;
n = s.size();
for (int i = 0; i < n; ++i)
{
f[i][i][to(s[i])] = 1;
}
for (int len = 2; len <= n; ++len)
{
for (int l = 0; l < n - len + 1; ++l)
{
int r = l + len - 1;
for (int k = l; k < r; ++k)
{
for (int u = 1; u <= 4; ++u)
{
for (int v = 1; v <= 4; ++v)
{
for (int w = 1; w <= 4; ++w)
{
if (a[u][v][w] && f[l][k][v] && f[k + 1][r][w])
f[l][r][u] = 1;
}
}
}
}
}
}
bool flag = 1;
if (f[0][n - 1][1])
printf("W"), flag = 0;
if (f[0][n - 1][2])
printf("I"), flag = 0;
if (f[0][n - 1][3])
printf("N"), flag = 0;
if (f[0][n - 1][4])
printf("G"), flag = 0;
if (flag)
{
printf("The name is wrong!");
}
return 0;
}
Index 5:
windy学会了一种游戏。
对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。
最开始windy把数字按顺序1,2,3,……,N写一排在纸上。
然后再在这一排下面写上它们对应的数字。
然后又在新的一排下面写上它们对应的数字。
如此反复,直到序列再次变为1,2,3,……,N。
如:
1 2 3 4 5 6
对应的关系为
1->2 2->3 3->1 4->5 5->4 6->6
windy的操作如下
1 2 3 4 5 6
2 3 1 5 4 6
3 1 2 4 5 6
1 2 3 5 4 6
2 3 1 4 5 6
3 1 2 5 4 6
1 2 3 4 5 6
这时,我们就有若干排1到N的排列,上例中有7排。
现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。
一个整数,N。
一个整数,可能的排数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int prime[N], cnt;
int phi[N];
bool is_prime[N];
void pre(int k)
{
memset(is_prime, 1, sizeof(is_prime));
is_prime[1] = 0;
phi[1] = 1;
for (int i = 2; i <= k; ++i)
{
if (is_prime[i])
{
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && i * prime[j] <= k; ++j)
{
is_prime[i * prime[j]] = 0;
if (i % prime[j])
{
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
else
{
phi[i * prime[j]] = phi[i] * prime[j];
}
}
}
}
int n;
int f[N];
signed main()
{
n = read();
pre(n);
f[0] = 1;
for (int i = 1; i <= cnt; ++i)
{
for (int j = n; j >= prime[i]; --j)
{
int k = prime[i];
while (k <= j)
{
f[j] += f[j - k];
k *= prime[i];
}
}
}
int ans = 0;
for (int i = 0; i <= n; ++i)
ans += f[i];
printf("%lld\n", ans);
return 0;
}
Index 6:
折叠的定义如下:
-
一个字符串可以看成它自身的折叠。记作
S = S
-
X(S)
是 个S
连接在一起的串的折叠。记作X(S) = SSSS…S
。 -
如果
A = A’
,B = B’
,则AB = A’B’
。例如:因为3(A) = AAA
,2(B) = BB
,所以3(A)C2(B) = AAACBB
,而2(3(A)C)2(B) = AAACAAACBB
给一个字符串,求它的最短折叠。
例如 AAAAAAAAAABABABCCD
的最短折叠为:9(A)3(AB)CCD
。
仅一行,即字符串 S
,长度保证不超过
仅一行,即最短的折叠长度。
正常区间DP很容易理解
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e2 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
char s[N];
int f[N][N], a[N];
bool check(int l, int r, int len)
{
for (int i = l; i <= r; ++i)
{
if (s[i] != s[(i - l) % len + l])
return false;
}
return true;
}
int main()
{
scanf("%s", s + 1);
n = strlen(s + 1);
for (int i = 1; i <= 9; ++i)
a[i] = 1;
for (int i = 10; i <= 99; ++i)
a[i] = 2;
a[100] = 3;
memset(f, 0x3f3f3f3f, sizeof(f));
for (int i = 1; i <= n; ++i)
f[i][i] = 1;
for (int len = 2; len <= n; ++len)
{
for (int l = 1; l <= n - len + 1; ++l)
{
int r = l + len - 1;
for (int k = l; k < r; ++k)
{
f[l][r] = min(f[l][k] + f[k + 1][r], f[l][r]);
}
for (int k = l; k < r; ++k)
{
int o = k - l + 1;
if (len % o)
continue;
if (check(l, r, o))
f[l][r] = min(f[l][k] + 2 + a[len / o], f[l][r]);
}
}
}
printf("%d\n", f[1][n]);
return 0;
}
Index 7:
Farmer John 的
每头奶牛都能为全队贡献一定量的牛奶,数值在
MMM 的目标是通过合作,增进家庭成员间的默契。为了支持比赛精神,奶牛们希望在赢得比赛的前提下,有尽可能多对奶牛间存在直系血缘关系。当然,所有奶牛都是女性,因此这里的直系血缘关系就是母女关系。
现在 FJ 摸清了所有奶牛间的血缘关系,希望算出一个团队在赢得胜利的前提下,最多有多少对奶牛存在血缘关系。注意:如果一个团队由某头奶牛和她的母亲和外祖母组成的话,这个团队只有两对血缘关系(她和她的母亲,她的母亲和外祖母)。
第一行两个整数
接下来
保证血缘关系不会出现环。
输出在获胜的前提下,一个团队中最多存在多少血缘关系。
特别地,如果不存在一个团队能获胜,输出
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 5e2 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, x;
vector<int> tree[N];
int a[N];
int f[N][N][2];
void dfs(int u)
{
f[u][0][0] = 0, f[u][0][1] = a[u];
for (int i = 0; i < tree[u].size(); ++i)
{
int v = tree[u][i];
dfs(v);
for (int j = n - 1; j >= 0; --j)
{
for (int k = j; k >= 0; --k)
f[u][j][0] = max(f[u][j][0], f[u][k][0] + max(f[v][j - k][0], f[v][j - k][1]));
for (int k = j; k >= 0; --k)
f[u][j][1] = max(f[u][j][1], f[u][k][1] + f[v][j - k][0]);
for (int k = j - 1; k >= 0; --k)
f[u][j][1] = max(f[u][j][1], f[u][k][1] + f[v][j - k - 1][1]);
}
}
}
int main()
{
memset(f, -0x3f3f, sizeof(f));
n = read(), x = read();
for (int i = 1; i <= n; ++i)
{
a[i] = read();
tree[read()].push_back(i);
}
dfs(0);
for (int i = n - 1; i >= 0; --i)
{
if (f[0][i][0] >= x)
{
printf("%d\n", i);
return 0;
}
}
puts("-1");
return 0;
}
Index 8:
某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。
从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。
现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。
写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。
输入文件的第一行包含两个用空格隔开的整数
第一个转播站即树的根结点编号为
接下来的
输出文件仅一行,包含一个整数,表示上述问题所要求的最大用户数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 3e3 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
struct edge
{
int to, dis, nxt;
} e[N << 1];
int head[N], cnt;
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int f[N][N], w[N];
int dfs(int u)
{
if (u > n - m)
{
f[u][1] = w[u];
return 1;
}
int res = 0;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].dis;
int q = dfs(v);
res += q;
for (int j = res; j >= 1; --j)
{
for (int k = 1; k <= q; ++k)
{
if (j - k >= 0)
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k] - w);
}
}
}
return res;
}
int main()
{
memset(f, -0x3f3f3f, sizeof(f));
n = read(), m = read();
for (int i = 1; i <= n - m; ++i)
{
int k = read();
for (int j = 1; j <= k; ++j)
{
int v = read(), w = read();
add_edge(i, v, w);
}
}
for (int i = n - m + 1; i <= n; ++i)
w[i] = read();
for (int i = 1; i <= n; ++i)
f[i][0] = 0;
dfs(1);
for (int i = m; i >= 1; --i)
if (f[1][i] >= 0)
{
printf("%d\n", i);
break;
}
return 0;
}
Index 9:
设有字符串 abcbcd
,则字符串abcb□cd
,□a□bcbcd□
和abcb□cd□
都是 □
代表空格字符。
如果
请你写一个程序,求出字符串
输入文件第一行为字符串
输出文件仅一行包含一个整数,表示所求得字符串
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 2e3 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
char a[N], b[N];
int n, m, k;
int f[N][N];
int main()
{
memset(f, 0x3f3f3f3f, sizeof(f));
scanf("%s%s", a + 1, b + 1);
k = read();
n = strlen(a + 1), m = strlen(b + 1);
f[0][0] = 0;
for (int i = 0; i <= n; ++i)
{
for (int j = 0; j <= m; ++j)
{
f[i][j] = min(f[i][j], min(i > 0 ? f[i - 1][j] + k : 0x3f3f3f3f, min(j > 0 ? f[i][j - 1] + k : 0x3f3f3f3f, i > 0 && j > 0 ? f[i - 1][j - 1] + abs(a[i] - b[j]) : 0x3f3f3f3f)));
}
}
printf("%d\n", f[n][m]);
return 0;
}
Index 10:
给你一个长度为
-
找到
,使得 ; -
将 它们 替换为
。
每轮操作之后,显然数组的长度会减小
第一行包含一个整数
第二行包含
共一行仅一个整数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 5e2 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int a[N];
int f[N][N], g[N][N];
int main()
{
memset(f, 0x3f3f3f3f, sizeof(f));
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
for (int i = 1; i <= n; ++i)
f[i][i] = 1, g[i][i] = a[i];
for (int len = 1; len <= n; ++len)
{
for (int l = 1; l <= n - len + 1; ++l)
{
int r = l + len - 1;
for (int k = l; k < r; ++k)
{
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
if (g[l][k] == g[k + 1][r] && f[l][k] == 1 && f[k + 1][r] == 1)
{
f[l][r] = 1, g[l][r] = g[l][k] + 1;
}
}
}
}
printf("%d\n", f[1][n]);
return 0;
}
状压DP
Index 1:
农场主
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是
第一行:两个整数
第
一个整数,即牧场分配总方案数除以
经典状压
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 20;
const int M = 5e3 + 50;
const int Mod = 1e8;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N];
vector<int> st;
void init()
{
for (int i = 0; i < (1 << m); ++i)
{
if (!(i & (i << 1)))
st.push_back(i);
}
}
int f[N][M];
int main()
{
n = read(), m = read();
init();
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
a[i] = (a[i] << 1) + read();
}
}
f[0][0] = 1;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j < st.size(); ++j)
{
if ((st[j] & a[i]) != st[j])
continue;
for (int k = 0; k < st.size(); ++k)
{
if ((st[k] & a[i - 1]) != st[k])
continue;
if ((st[k] & st[j]) != 0)
continue;
f[i][st[j]] = (f[i][st[j]] + f[i - 1][st[k]]) % Mod;
}
}
}
int ans = 0;
for (int i = 0; i < st.size(); ++i)
ans = (ans + f[n][st[i]]) % Mod;
printf("%d\n", ans);
return 0;
}
Index 2:
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
所得的方案数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 20;
const int M = 2e3 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
vector<int> st;
int cont(int n)
{
int res = 0;
while (n)
{
res += n % 2;
n /= 2;
}
return res;
}
void init()
{
for (int i = 0; i < (1 << n); ++i)
{
if ((i & (i << 1)) != 0)
continue;
st.push_back(i);
}
}
int f[N][M][N];
signed main()
{
n = read(), m = read();
init();
f[0][0][0] = 1;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j < st.size(); ++j)
{
for (int k = 0; k < st.size(); ++k)
{
for (int u = cont(st[j]); u <= m; ++u)
{
if ((st[j] & st[k]) != 0)
continue;
int res = st[j] | st[k];
if ((res & (res << 1)) != 0)
continue;
f[i][st[j]][u] += f[i - 1][st[k]][u - cont(st[j])];
}
}
}
}
int ans = 0;
for (int i = 0; i < st.size(); ++i)
ans += f[n][st[i]][m];
printf("%lld\n", ans);
return 0;
}
树形结构
简单树上问题
Index 1:
求一棵树的重心,即找一点使其最大子树的节点数最小。
现在给你一棵树,要你求其重心,如果有多个请全部输出。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
struct edge
{
int to, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int siz[N];
int ans[N], res, maxn = 0x3f3f3f3f;
void dfs(int u, int p)
{
siz[u] = 1;
int Max = 0;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p)
continue;
dfs(v, u);
siz[u] += siz[v];
Max = max(Max, siz[v]);
}
Max = max(Max, n - siz[u]);
if (Max < maxn)
{
maxn = Max;
res = 1;
ans[res] = u;
}
else if (Max == maxn)
{
ans[++res] = u;
}
}
int main()
{
n = read();
for (int i = 1; i < n; ++i)
{
int u = read(), v = read();
add_edge(u, v);
add_edge(v, u);
}
dfs(1, 0);
sort(ans + 1, ans + res + 1);
for (int i = 1; i <= res; ++i)
printf("%d ", ans[i]);
return 0;
}
Index 2:
求一棵树的直径,即最远两点的距离。
现在给你一棵树,求它的直径。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
struct edge
{
int to, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int dis[N];
void dfs(int u, int p, int dep)
{
dis[u] = dep;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p)
continue;
dfs(v, u, dep + 1);
}
}
int main()
{
n = read();
for (int i = 1; i < n; ++i)
{
int u = read(), v = read();
add_edge(u, v);
add_edge(v, u);
}
dfs(1, 0, 0);
int Maxn = 0, Maxi;
for (int i = 1; i <= n; ++i)
{
if (dis[i] > Maxn)
{
Maxn = dis[i];
Maxi = i;
}
}
dfs(Maxi, 0, 0);
Maxn = 0;
for (int i = 1; i <= n; ++i)
Maxn = max(Maxn, dis[i]);
printf("%d\n", Maxn);
return 0;
}
树上分治
Index 1:
给你一棵树,要你求其中距离不大于
Aim 1:分治思想
这里的分治属于点分治
分治是分部解决一整个问题,我们考虑它的划分阶段,不难想到应当以子树的重心为划分点,这样我们可以保证它的时间复杂度达到
Aim 2:阶段问题
对于每一阶段应当处理的问题。
我们可以先预处理出所有节点除重心外到重心的距离。
将距离数组定义为dis[]
然后我们可以将原问题转化为
Q:这应当如何快速解决?
A:利用双指针的思想,先将数组排序,让后不断移动左右指针,累计答案。
Q:但是这么一想不会有重复的计算吗?
A:很好,所以我们应当减去以重心为根的子树的儿子节点的答案。
想必,您根据上书提问已经了解了问题的精髓所在,所以快去练练吧!这里是我的代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, k;
struct edge
{
int to, dis, nxt;
} e[N];
int head[N], cnt;
bool vis[N];
int dis[N];
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int res, root, Min;
int siz[N];
void dfs_siz(int u, int p)
{
siz[u] = 1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p || vis[v])
continue;
dfs_siz(v, u);
siz[u] += siz[v];
}
}
void dfs_root(int u, int p, int s)
{
int Max = s - siz[u];
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p || vis[v])
continue;
Max = max(Max, siz[v]);
dfs_root(v, u, s);
}
if (Max < Min)
{
Min = Max;
root = u;
}
}
void dfs_dis(int u, int p, int d)
{
dis[++res] = d;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p || vis[v])
continue;
dfs_dis(v, u, d + e[i].dis);
}
}
int sol(int u, int d)
{
res = 0;
dfs_dis(u, 0, d);
sort(dis + 1, dis + res + 1);
int tot = 0, L = 1, R = res;
while (L < R)
{
while (L < R && dis[L] + dis[R] > k)
R--;
tot += R - L;
++L;
}
return tot;
}
int ans;
void dfs(int u)
{
Min = 0x3f3f3f3f;
dfs_siz(u, 0);
dfs_root(u, 0, siz[u]);
ans += sol(root, 0);
vis[root] = 1;
for (int i = head[root]; i; i = e[i].nxt)
{
int v = e[i].to;
if (vis[v])
continue;
ans -= sol(v, e[i].dis);
dfs(v);
}
}
void work()
{
memset(head, 0, sizeof(head));
memset(dis, 0, sizeof(dis));
memset(vis, 0, sizeof(vis));
for (int i = 1; i < n; ++i)
{
int u = read(), v = read(), w = read();
add_edge(u, v, w);
add_edge(v, u, w);
}
dfs(1);
printf("%d\n", ans);
ans = cnt = 0;
}
int main()
{
while (1)
{
n = read(), k = read();
if (n == 0 && k == 0)
break;
work();
}
}
图论
默认最基本的模板以及变形您已经掌握,若没有立即退出阅读此章节,学习后阅读。
2022 年的CSP-S,大家也看到了,图论从老师口中的“最多考一题”,结果一跃成为考试的主要考点。所以学习图论的重要性十分重要。
拓扑排序
概念引入:DAG 有向无环图。
- 算法实现过程
- 选择入度为
的点作为起点。 - 删除该顶点以及以她为顶点的所有有向边。
- 重复以上操作。
- 如果图空结束且无环;否则有环无拓扑排序。
显然对于任意一个 DAG 她的拓扑排序可能不唯一。
Index 1:
给你一个食物网,你要求出这个食物网中最大食物链的数量。
(这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。)
Delia 非常急,所以你只有
由于这个结果可能过大,你只需要输出总数模上
第一行,两个正整数
接下来
一行一个整数,为最大食物链数量模上
各测试点满足以下约定:
数据中不会出现环,满足生物学的要求。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 5e3 + 50;
const int M = 1e6 + 50;
const int Mod = 80112002;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
struct edge
{
int to, nxt;
} e[M];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int in[N], out[N];
int f[N];
void topo()
{
queue<int> q;
for (int i = 1; i <= n; ++i)
{
if (in[i])
continue;
q.push(i);
f[i] = 1;
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
in[v]--;
f[v] = (f[v] + f[u]) % Mod;
if (!in[v])
q.push(v);
}
}
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read();
in[v]++, out[u]++;
add_edge(u, v);
}
topo();
int ans = 0;
for (int i = 1; i <= n; ++i)
if (!out[i])
ans = (ans + f[i]) % Mod;
printf("%d\n", ans);
return 0;
}
Index 2:
小明要去一个国家旅游。这个国家有#
所以他就需要选择最先到达的城市,并制定一条路线以城市i为终点,使得线路上除了第一个城市,每个城市都在路线前一个城市东面,并且满足这个前提下还希望游览的城市尽量多。
现在,你只知道每一条道路所连接的两个城市的相对位置关系,但并不知道所有城市具体的位置。现在对于所有的i,都需要你为小明制定一条路线,并求出以城市
第
接下来
对于
对于
对于
发现是个DAG
但凡你有点地理常识。
那么,拓扑走起。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
struct node
{
int to, nxt;
} e[N << 1];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int f[N];
int in[N];
void topo()
{
queue<int> q;
for (int i = 1; i <= n; ++i)
if (!in[i])
q.push(i), f[i] = 1;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
--in[v];
f[v] = max(f[v], f[u] + 1);
if (!in[v])
q.push(v);
}
}
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read();
add_edge(u, v);
in[v]++;
}
topo();
for (int i = 1; i <= n; ++i)
{
printf("%d\n", f[i]);
}
return 0;
}
Index 3:
John
的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务John
有需要完成的
写一个程序从John
的农场有足够多的工人来同时完成任意多项任务。
第1行:一个整数
第
* 工作序号(
* 完成工作所需要的时间
* 一些必须完成的准备工作,总数不超过
一个整数,表示完成所有杂务所需的最短时间。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e6 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
struct node
{
int to, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int in[N], vol[N];
int f[N];
void topo()
{
queue<int> q;
for (int i = 1; i <= n; ++i)
if (!in[i])
q.push(i), f[i] = vol[i];
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
in[v]--;
f[v] = max(f[v], f[u] + vol[v]);
if (!in[v])
q.push(v);
}
}
}
int main()
{
n = read();
for (int i = 1; i <= n; ++i)
{
int u = read(), v;
vol[i] = read();
while (1)
{
v = read();
if (!v)
break;
add_edge(v, u);
in[u]++;
}
}
topo();
int ans = 0;
for (int i = 1; i <= n; ++i)
ans = max(ans, f[i]);
printf("%d\n", ans);
return 0;
}
Index 4:
一条单向的铁路线上,依次有编号为
例如,下表是$ 5
现有
第一行包含
第
一个正整数,即
对于
对于
对于
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e3 + 50;
const int M = 2e6 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
bool vis[N];
bool rep[N][N];
int a[N], in[N];
struct node
{
int to, nxt;
} e[M];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
queue<int> q;
int f[N];
void topo()
{
int ans = 0;
for (int i = 1; i <= n; ++i)
if (!in[i])
q.push(i);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
f[v] = f[u] + 1;
ans = max(ans, f[v]);
in[v]--;
if (!in[v])
q.push(v);
}
}
printf("%d\n", ans + 1);
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= m; ++i)
{
memset(vis, 0, sizeof(vis));
int nn = read();
for (int j = 1; j <= nn; ++j)
a[j] = read(), vis[a[j]] = 1;
for (int j = a[1]+1; j <= a[nn]; ++j)
{
if (!vis[j])
{
for (int k = 1; k <= nn; ++k)
{
if (!rep[j][a[k]])
{
rep[j][a[k]] = 1;
add_edge(j, a[k]);
in[a[k]]++;
}
}
}
}
}
topo();
return 0;
}
欧拉路
Q:什么是欧拉路
A:类似一笔画
引入概念:欧拉回路指一笔画的起点与终点相同。
Index 1:
题目大意:有一种由彩色珠子连接而成的项链。每个珠子的两半由不同颜色组成。如图所示,相邻两个珠子在接触的地方颜色相同。现在有一些零碎的珠子,需要确认它们是否可以复原成完整的项链。
输入格式:输入第一行为测试数据组数
输出格式:对于每组数据,输出测试数据编号和方案。如果无解,输出some beads may be lost
。方案的格式和输入相同,也是一共
输出任意欧拉回路不是此题的难点
难点在于如何判断是否有路
Aim 1:欧拉性质
环境为连通图
研究表明,如果一个图所有点的度为偶数,则存在欧拉回路。
度即入度与出度之和,换而言之,此节点连接的边数。
tip: 将珠子看成边。
暂时弃坑:2023-1-16
分层图
分层图适用于解决最短路上有额外条件的问题。
Index 1:
我们考虑最简单的旅行问题吧: 现在这个大陆上有
这不就是最短路问题吗?我们都知道可以用 Dijkstra、Bellman-Ford、Floyd-Warshall等算法来解决。
现在,我们一共有
- 在一条道路上最多只能使用一张 SpellCard。
- 使用一张SpellCard 只在一条道路上起作用。
- 你不必使用完所有的 SpellCard。
给定以上的信息,你的任务是:求出在可以使用这不超过
第一行包含三个整数:
接下来
输出一个整数,表示从
对于
, 。 , 。- 为保证答案为整数,保证所有的
均为偶数。 - 所有数据中的无向图保证无自环、重边,且是连通的。
Aim 1:如何分层
我们需要将第
具体细节看代码,最短路不用讲的,所以这题难点只在建边的过程中。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m, k;
struct edge
{
int to, dis, nxt;
} e[N << 1];
int head[N], cnt;
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
struct node
{
int dis, pos;
bool operator<(const node &x) const
{
return x.dis < dis;
}
};
priority_queue<node> q;
int dis[N];
bool vis[N];
void dij()
{
memset(dis, 0x3f3f3f3f, sizeof(dis));
dis[1] = 0;
q.push((node){0, 1});
while (!q.empty())
{
node tmp = q.top();
q.pop();
int x = tmp.pos;
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
if (dis[y] > dis[x] + e[i].dis)
{
dis[y] = dis[x] + e[i].dis;
if (!vis[y])
{
q.push((node){dis[y], y});
}
}
}
}
int res = Mod;
for (int i = 0; i <= k; ++i)
{
res = min(res, dis[i * n + n]);
}
printf("%d\n", res);
}
int main()
{
n = read(), m = read(), k = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read(), w = read();
for (int j = 0; j <= k; ++j)
{
add_edge(j * n + u, j * n + v, w);
add_edge(j * n + v, j * n + u, w);
add_edge(j * n + u, (j + 1) * n + v, w / 2);
add_edge(j * n + v, (j + 1) * n + u, w / 2);
}
}
dij();
return 0;
}
Index 2:
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多
第一行三个整数
接下来一行两个整数
接下来
输出一行一个整数,为最少花费。
对于
做分层图的题,一定要注意数据范围,血的教训。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 3e6 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m, k;
int s, t;
struct edge
{
int to, dis, nxt;
} e[N << 1];
int head[N], cnt;
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int dis[N];
bool vis[N];
struct node
{
int pos, dis;
bool operator<(const node &x) const
{
return x.dis < dis;
}
};
priority_queue<node> q;
void dij(int s)
{
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
q.push((node){s, 0});
while (!q.empty())
{
node tmp = q.top();
q.pop();
int x = tmp.pos;
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
if (dis[y] > dis[x] + e[i].dis)
{
dis[y] = dis[x] + e[i].dis;
if (!vis[y])
{
q.push((node){y, dis[y]});
}
}
}
}
int res = Mod;
for (int i = 0; i <= k; ++i)
{
res = min(res, dis[t + i * n]);
}
printf("%d\n", res);
}
int main()
{
n = read(), m = read(), k = read();
s = read(), t = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read(), w = read();
add_edge(u, v, w);
add_edge(v, u, w);
for (int j = 1; j <= k; ++j)
{
add_edge(u + j * n, v + j * n, w);
add_edge(v + j * n, u + j * n, w);
add_edge(u + (j - 1) * n, v + j * n, 0);
add_edge(v + (j - 1) * n, u + j * n, 0);
}
}
dij(s);
return 0;
}
Index 3:
多年以后,笨笨长大了,成为了电话线布置师。由于地震使得某市的电话线全部损坏,笨笨是负责接到震中市的负责人。该市周围分布着
第i对电线杆的两个端点分别是
电信公司决定支援灾区免费为此市连接
请你计算一下,将电话线引导震中市最少需要在电话线上花多少钱?
输入文件的第一行包含三个数字
第二行到第
一个整数,表示该项工程的最小支出,如果不可能完成则输出 -1
。
最短路变形,求最大值,一定要看清题目,否则会像我一样挂分。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 2e6 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m, k;
struct edge
{
int to, dis, nxt;
} e[N << 1];
int head[N], cnt;
bool vis[N];
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int dis[N];
struct node
{
int pos, dis;
bool operator<(const node &x) const
{
return x.dis < dis;
}
};
priority_queue<node> q;
void dij()
{
memset(dis, 0x3f3f3f3f, sizeof(dis));
dis[1] = 0;
q.push((node){1, 0});
while (!q.empty())
{
node tmp = q.top();
q.pop();
int x = tmp.pos;
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
if (dis[y] > max(dis[x], e[i].dis))
{
dis[y] = max(dis[x], e[i].dis);
if (!vis[y])
{
q.push((node){y, dis[y]});
}
}
}
}
int Min = 0x3f3f3f3f;
for (int i = 0; i <= k; ++i)
{
Min = min(Min, dis[i * n + n]);
}
if (Min == 0x3f3f3f3f)
{
printf("-1\n");
}
else
{
printf("%d\n", Min);
}
}
int main()
{
n = read(), m = read(), k = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read(), w = read();
add_edge(u, v, w);
add_edge(v, u, w);
for (int j = 1; j <= k; ++j)
{
add_edge(j * n + u, j * n + v, w);
add_edge(j * n + v, j * n + u, w);
add_edge((j - 1) * n + u, j * n + v, 0);
add_edge((j - 1) * n + v, j * n + u, 0);
}
}
dij();
return 0;
}
高级数据结构
分块
优雅+暴力
kawayi~~
Index 1:
给出一个长为
第一行输入一个数字
第二行输入
接下来输入 n 行询问,每行输入四个数字
若
若
对于
分块的基本思想,整体的一起解决,琐碎的暴力解决,尽可能地压缩时间。
但是我们需要注意分块地适应范围可以说很广也可以说很窄,广在可以运用在方方面面,窄在数据范围地要求较高。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int a[N], b[N];
int add[N];
int block,num;
int pos[N];
int L[N], R[N];
void update(int x)
{
for (int i = L[pos[x]]; i <= R[pos[x]]; ++i)
b[i] = a[i];
sort(b + L[pos[x]], b + R[pos[x]] + 1);
}
int main()
{
n = read();
block = sqrt(n);
num=n/block;
if (n % block)
num++;
for (int i = 1; i <= n; ++i)
a[i] = read(), b[i] = a[i];
for (int i = 1; i <= num; ++i)
L[i] = (i - 1) * block + 1, R[i] = i * block;
R[num] = n;
for (int i = 1; i <= n; ++i)
pos[i] = (i - 1) / block + 1;
for (int i = 1; i <= num; ++i)
{
sort(b + L[i], b + R[i] + 1);
}
for (int i = 1; i <= n; ++i)
{
int opt = read(), l = read(), r = read(), c = read();
if (opt == 0)
{
if (pos[l] == pos[r])
{
for (int i = l; i <= r; ++i)
a[i] += c;
update(l);
}
else
{
for (int i = l; i <= R[pos[l]]; ++i)
a[i] += c;
update(l);
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
add[i] += c;
for (int i = L[pos[r]]; i <= r; ++i)
a[i] += c;
update(r);
}
}
else
{
c *= c;
int ans = 0;
if (pos[l] == pos[r])
{
for (int i = l; i <= r; ++i)
if (a[i] + add[pos[l]] < c)
++ans;
}
else
{
for (int i = l; i <= R[pos[l]]; ++i)
if (a[i] + add[pos[l]] < c)
++ans;
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
{
int it = lower_bound(b + L[i], b + R[i] + 1, c - add[i]) - b - 1;
ans += it - L[i] + 1;
}
for (int i = L[pos[r]]; i <= r; ++i)
if (a[i] + add[pos[r]] < c)
++ans;
}
printf("%d\n", ans);
}
}
return 0;
}
Index 2:
给出一个长为
第一行输入一个数字
第二行输入
接下来输入 n 行询问,每行输入四个数字
若
若
对于
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N], b[N];
int add[N];
int block, num;
int pos[N];
int L[N], R[N];
void update(int x)
{
for (int i = L[pos[x]]; i <= R[pos[x]]; ++i)
b[i] = a[i];
sort(b + L[pos[x]], b + R[pos[x]] + 1);
}
signed main()
{
n = read();
block = sqrt(n);
num = n / block;
if (n % block)
num++;
for (int i = 1; i <= n; ++i)
a[i] = read(), b[i] = a[i];
for (int i = 1; i <= num; ++i)
L[i] = (i - 1) * block + 1, R[i] = i * block;
R[num] = n;
for (int i = 1; i <= n; ++i)
pos[i] = (i - 1) / block + 1;
for (int i = 1; i <= num; ++i)
{
sort(b + L[i], b + R[i] + 1);
}
m = n;
while (m--)
{
int opt = read(), l = read(), r = read(), c = read();
if (opt == 0)
{
if (pos[l] == pos[r])
{
for (int i = l; i <= r; ++i)
a[i] += c;
update(l);
}
else
{
for (int i = l; i <= R[pos[l]]; ++i)
a[i] += c;
update(l);
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
add[i] += c;
for (int i = L[pos[r]]; i <= r; ++i)
a[i] += c;
update(r);
}
}
else
{
int ans = -1;
if (pos[l] == pos[r])
{
for (int i = l; i <= r; ++i)
if (a[i] + add[pos[l]] < c)
ans = max(ans, a[i] + add[pos[l]]);
}
else
{
for (int i = l; i <= R[pos[l]]; ++i)
if (a[i] + add[pos[l]] < c)
ans = max(ans, a[i] + add[pos[l]]);
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
{
int it = lower_bound(b + L[i], b + R[i] + 1, c - add[i]) - b - 1;
if (it >= L[i])
{
ans = max(ans, b[it] + add[i]);
}
}
for (int i = L[pos[r]]; i <= r; ++i)
if (a[i] + add[pos[r]] < c)
ans = max(ans, a[i] + add[pos[r]]);
}
printf("%lld\n", ans);
}
}
return 0;
}
Index 3:
给出一个长为
第一行输入一个数字
第二行输入
接下来输入
若
若
对于
警示后人:不要没事干就
草mod
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 5e4 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N];
int sum[N], add[N];
int block, num;
int L[N], R[N];
int pos[N];
signed main()
{
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
block = sqrt(n);
num = n / block;
if (n % block)
++num;
for (int i = 1; i <= num; ++i)
L[i] = (i - 1) * block + 1, R[i] = i * block;
R[num] = n;
for (int i = 1; i <= num; ++i)
for (int j = L[i]; j <= R[i]; ++j)
pos[j] = i, sum[i] += a[j];
m = n;
while (m--)
{
int opt = read(), l = read(), r = read(), c = read();
if (opt == 0)
{
if (pos[l] == pos[r])
{
sum[pos[l]] += (r - l + 1) * c;
for (int i = l; i <= r; ++i)
a[i] += c;
}
else
{
sum[pos[l]] += (R[pos[l]] - l + 1) * c;
for (int i = l; i <= R[pos[l]]; ++i)
a[i] += c;
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
add[i] += c;
sum[pos[r]] += (r - L[pos[r]] + 1) * c;
for (int i = L[pos[r]]; i <= r; ++i)
a[i] += c;
}
}
else
{
++c;
int ans = 0;
if (pos[l] == pos[r])
{
for (int i = l; i <= r; ++i)
ans += a[i];
ans += (r - l + 1) * add[pos[l]];
ans %= c;
}
else
{
for (int i = l; i <= R[pos[l]]; ++i)
ans += a[i];
ans += (R[pos[l]] - l + 1) * add[pos[l]];
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
ans += sum[i] + (R[i] - L[i] + 1) * add[i];
for (int i = L[pos[r]]; i <= r; ++i)
ans += a[i];
ans += (r - L[pos[r]] + 1) * add[pos[r]];
ans %= c;
}
printf("%lld\n", ans);
}
}
return 0;
}
Index 4:
给出一个长为
第一行输入一个数字
第二行输入
接下来输入
若
若
若
对于
在处理碎片化数据时,遍历整个块,修改数组值。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 10007;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N];
int pos[N];
int L[N], R[N];
int block, num;
int add[N], mul[N];
void push_down(int x)
{
for (int i = L[pos[x]]; i <= R[pos[x]]; ++i)
a[i] *= mul[pos[i]], a[i] += add[pos[i]], a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
add[pos[x]] = 0;
mul[pos[x]] = 1;
}
signed main()
{
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
block = sqrt(n);
num = n / block;
if (n % block)
++num;
for (int i = 1; i <= num; ++i)
L[i] = (i - 1) * block + 1, R[i] = i * block, mul[i] = 1;
R[num] = n;
for (int i = 1; i <= num; ++i)
{
for (int j = L[i]; j <= R[i]; ++j)
pos[j] = i;
}
m = n;
while (m--)
{
int opt = read(), l = read(), r = read(), c = read() % Mod;
if (opt == 0)
{
if (pos[l] == pos[r])
{
push_down(l);
for (int i = l; i <= r; ++i)
a[i] += c, a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
}
else
{
push_down(l);
for (int i = l; i <= R[pos[l]]; ++i)
a[i] += c, a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
add[i] += c;
push_down(r);
for (int i = L[pos[r]]; i <= r; ++i)
a[i] += c, a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
}
}
else if (opt == 1)
{
if (pos[l] == pos[r])
{
push_down(l);
for (int i = l; i <= r; ++i)
a[i] *= c, a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
}
else
{
push_down(l);
for (int i = l; i <= R[pos[l]]; ++i)
a[i] *= c, a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
mul[i] *= c, add[i] *= c, mul[i] = mul[i] >= Mod ? mul[i] % Mod : mul[i], add[i] = add[i] > Mod ? add[i] % Mod : add[i];
push_down(r);
for (int i = L[pos[r]]; i <= r; ++i)
a[i] *= c, a[i] = a[i] >= Mod ? a[i] % Mod : a[i];
}
}
else
{
int ans = a[r];
ans *= mul[pos[r]];
ans += add[pos[r]];
ans = ans >= Mod ? ans % Mod : ans;
printf("%lld\n", ans);
}
}
return 0;
}
Index 5:
给出一个长为
第一行输入一个数字
第二行输入
接下来输入
表示先查询位于
对于
分块的思想就是暴力,所以还是按原来的写。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N];
int pos[N];
int L[N], R[N];
int block, num;
int alt[N];
void push_down(int x)
{
if (alt[pos[x]] == -1)
return;
for (int i = L[pos[x]]; i <= R[pos[x]]; ++i)
a[i] = alt[pos[x]];
alt[pos[x]] = -1;
}
int main()
{
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
block = sqrt(n);
num = n / block;
if (n % block)
++num;
for (int i = 1; i <= num; ++i)
L[i] = (i - 1) * block + 1, R[i] = i * block;
R[num] = n;
for (int i = 1; i <= num; ++i)
{
for (int j = L[i]; j <= R[i]; ++j)
pos[j] = i;
alt[i] = -1;
}
m = n;
while (m--)
{
int l = read(), r = read(), c = read();
int ans = 0;
if (pos[l] == pos[r])
{
if (alt[pos[l]] != -1)
{
if (alt[pos[l]] == c)
ans += r - l + 1;
}
else
{
for (int i = l; i <= r; ++i)
ans += (a[i] == c);
}
}
else
{
if (alt[pos[l]] != -1)
{
if (alt[pos[l]] == c)
ans += R[pos[l]] - l + 1;
}
else
{
for (int i = l; i <= R[pos[l]]; ++i)
ans += (a[i] == c);
}
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
{
if (alt[i] != -1)
{
if (alt[i] == c)
ans += R[i] - L[i] + 1;
}
else
{
for (int j = L[i]; j <= R[i]; ++j)
ans += (a[j] == c);
}
}
if (alt[pos[r]] != -1)
{
if (alt[pos[r]] == c)
ans += r - L[pos[r]] + 1;
}
else
{
for (int i = L[pos[r]]; i <= r; ++i)
ans += (a[i] == c);
}
}
printf("%d\n", ans);
if (pos[l] == pos[r])
{
push_down(l);
for (int i = l; i <= r; ++i)
a[i] = c;
}
else
{
push_down(l);
for (int i = l; i <= R[pos[l]]; ++i)
a[i] = c;
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i)
alt[i] = c;
push_down(r);
for (int i = L[pos[r]]; i <= r; ++i)
a[i] = c;
}
}
return 0;
}
RMQ
此类型唯一的用途是查询区间极值。
限制不可修改,范围由于倍增的原因,时间极低,适应能力强。
Index 1:
每天,农夫 John 的
有一天, John 决定让一些牛们玩一场飞盘比赛。他准备找一群在队列中位置连续的牛来进行比赛。但是为了避免水平悬殊,牛的身高不应该相差太大。John 准备了
第一行两个数
接下来
再接下来
输出共
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, q;
int a[N];
int Min[N][30];
int Max[N][30];
int query_max(int l, int r)
{
int k = log2(r - l + 1);
return max(Max[l][k], Max[r - (1 << k) + 1][k]);
}
int query_min(int l, int r)
{
int k = log2(r - l + 1);
return min(Min[l][k], Min[r - (1 << k) + 1][k]);
}
int main()
{
n = read(), q = read();
for (int i = 1; i <= n; ++i)
a[i] = read(), Min[i][0] = Max[i][0] = a[i];
for (int j = 1; (1 << j) <= n; ++j)
{
for (int i = 1; i + (1 << j) - 1 <= n; ++i)
{
Min[i][j] = min(Min[i][j - 1], Min[i + (1 << j - 1)][j - 1]);
Max[i][j] = max(Max[i][j - 1], Max[i + (1 << j - 1)][j - 1]);
}
}
while (q--)
{
int l = read(), r = read();
printf("%d\n", query_max(l, r) - query_min(l, r));
}
return 0;
}
是不是觉得RMQ很没用
但是如果没有修改的话你写线段树,岂不是大材小用,不仅代码拥挤,也不容易调试。
树状数组
用于区间加速查询,修改,强有力的时间效率,简单而又精悍的代码,巧妙的思维带您领略数据结构的入门篇章。
tree[] 表示树状数组
k 表示 i 二进制下末尾 0 的个数
那么
建议手动枚举前几位寻找其规律。
Index 1:
输入数据的第一行为 X n m
,代表矩阵大小为
从输入数据的第二行开始到文件尾的每一行会出现以下两种操作:
L a b c d delta
—— 代表将 为顶点的矩形区域内的所有数字加上 。k a b c d
—— 代表求 为顶点的矩形区域内所有数字的和。
针对每个
对于
二维区间修改+区间查询
Aim 1: 区间修改
为原数组的差分数组。
Aim 2: 区间查询
因此维护四个树状数组,维护
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 2050;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int tree1[N][N], tree2[N][N], tree3[N][N], tree4[N][N];
int lowbit(int x) { return x & (-x); }
void update(int x, int y, int d)
{
for (int i = x; i <= n; i += lowbit(i))
{
for (int j = y; j <= m; j += lowbit(j))
{
tree1[i][j] += d;
tree2[i][j] += d * x;
tree3[i][j] += d * y;
tree4[i][j] += d * x * y;
}
}
}
int query(int x, int y)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
for (int j = y; j > 0; j -= lowbit(j))
{
res += (x + 1) * (y + 1) * tree1[i][j] - (y + 1) * tree2[i][j] - (x + 1) * tree3[i][j] + tree4[i][j];
}
}
return res;
}
char ch[2];
int main()
{
n = read(), m = read();
while (scanf("%s", ch) != EOF)
{
if (ch[0] == 'L')
{
int a = read(), b = read(), c = read(), d = read(), delta = read();
update(a, b, delta);
update(c + 1, b, -delta);
update(a, d + 1, -delta);
update(c + 1, d + 1, delta);
}
else
{
int a = read(), b = read(), c = read(), d = read();
printf("%d\n", query(c, d) - query(c, b - 1) - query(a - 1, d) + query(a - 1, b - 1));
}
}
return 0;
}
Index 2:
对于给定的一段正整数序列,逆序对就是序列中
第一行,一个数
第二行
对于
单点修改+区间查询
Aim 1: 如何转化
我们从大到小依次添加,在相应位置加
每次答案为这个位置前的数之和,可以用树状数组解决。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 5e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
struct node
{
int ord, vol;
} a[N];
bool cmp(node x, node y)
{
return x.vol >= y.vol;
}
int tree[N];
int lowbit(int x) { return x & (-x); }
void update(int x, int d)
{
for (int i = x; i <= n; i += lowbit(i))
{
tree[i] += d;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
res += tree[i];
}
return res;
}
signed main()
{
n = read();
for (int i = 1; i <= n; ++i)
a[i].ord = i, a[i].vol = read();
stable_sort(a + 1, a + n + 1, cmp);
int ans = 0;
for (int i = 1; i <= n; ++i)
{
update(a[i].ord, 1);
ans += query(a[i].ord - 1);
}
printf("%lld\n", ans);
return 0;
}
Index 3:
有一个 n 个元素的数组,每个元素初始均为 0。
第一行包含两个整数 n, m,表示数组的长度和指令的条数; 以下 m 行,每行的第一个数 t 表示操作的种类.
若 t = 1,则接下来有两个数 L R
,表示区间 [L, R] 的每个数均反转; 若 t = 2,则接下来只有一个数 i
,表示询问的下标。
对于
Aim 1:简易变形
我们可以发现,原来的差分修改只是变成了异或修改。
此时翻转
第
xor 表示 异或运算。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int tree[N];
int lowbit(int x) { return x & (-x); }
void update(int x)
{
for (int i = x; i <= n; i += lowbit(i))
{
tree[i] ^= 1;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
res ^= tree[i];
}
return res;
}
int main()
{
n = read(), m = read();
while (m--)
{
int opt = read();
if (opt == 1)
{
int l = read(), r = read();
update(l);
update(r + 1);
}
else
{
int q = read();
printf("%d\n", query(q));
}
}
return 0;
}
Index 4:
第一行为两个整数
接下来有
- 若
,则表示 SCV 在 这段区间布上一种地雷; - 若
,则表示小 FF 询问当前 区间总共有多少种地雷。
对于
Aim 1:快速统计
就是当前的开头-结尾。
That's enough!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int tree1[N], tree2[N];
int lowbit(int x)
{
return x & (-x);
}
void update_start(int x, int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
tree1[i] += k;
}
}
void update_end(int x, int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
tree2[i] += k;
}
}
int query_start(int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
res += tree1[i];
}
return res;
}
int query_end(int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
res += tree2[i];
}
return res;
}
int main()
{
n = read(), m = read();
while (m--)
{
int opt = read(), l = read(), r = read();
if (opt == 1)
{
update_start(l, 1);
update_end(r, 1);
}
else
{
printf("%d\n", query_start(r) - query_end(l - 1));
}
}
return 0;
}
Index 5:
一种名为 Tetris Attack 的猜谜游戏风靡 Byteotia。游戏本身非常复杂,因此我们只介绍它的简化规则:
玩家拥有一个有
玩家可以交换两个相邻元素,即互换他们的位置。交换后,如果有两个相邻的元素标有相同的符号,则将他们都从栈中删除。然后,位于其上方的所有元素都会掉落下来,并且可以造成再次删除。
玩家的目标是以最少的移动次数清空堆栈。请你编写一个程序,找出最少的移动次数及方案。
第一行一个整数
接下来的
最初不存在相邻的两个元素符号相同的情况,保证有不超过
第一行一个整数
接下来
第
如果存在多个方案,则输出其中任何一个。
相当于栈遇到一对立即执行,它会做出最大贡献。
因为消掉两个方块只会对其它方块的距离产生变化,用树状数组维护区间,调换方案循环输出即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int a[N];
int tree[N];
int lowbit(int x) { return x & (-x); }
void update(int x, int k)
{
for (int i = x; i <= 2 * n; i += lowbit(i))
{
tree[i] += k;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
res += tree[i];
}
return res;
}
int b[N];
queue<int>q;
int main()
{
n = read();
for (int i = 1; i <= 2 * n; ++i)
{
a[i] = read();
}
int ans = 0;
for (int i = 1; i <= 2 * n; ++i)
{
if (!b[a[i]])
b[a[i]] = i;
else
{
int k=i - b[a[i]] - 1 + query(i) - query(b[a[i]] - 1);
ans+=k;
int l=b[a[i]]+query(b[a[i]]);
for(int i=l;i<=l+k-1;++i) q.push(i);
update(i, -1);
update(b[a[i]], -1);
}
}
printf("%d\n", ans);
while(!q.empty()){
printf("%d\n",q.front());
q.pop();
}
return 0;
}
线段树
广受出题人和OIer们青睐的高级数据结构,更高效的区间操作,更巧妙的构造思想,携起手来,跨入OI新篇章。
Index 1:
也就是
现在有「两种」操作
0 x y
把区间
1 x y
询问区间
有多组数据,数据以EOF结束,对于每组数据,输出数据的序号,每组数据之后输出一个空行。
不保证给出的区间
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int tree[N], a[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree[p] = tree[ls(p)] + tree[rs(p)];
}
void build(int p, int l, int r)
{
if (l == r)
{
tree[p] = a[l];
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void update(int nx, int ny, int l, int r, int p)
{
if (nx <= l && r <= ny)
{
if (r - l + 1 >= tree[p])
return;
}
if (l == r)
{
tree[p] = (int)sqrt(tree[p]);
return;
}
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p));
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p));
push_up(p);
}
int query(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return tree[p];
int mid = l + r >> 1;
if (nx <= mid)
res += query(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query(nx, ny, mid + 1, r, rs(p));
return res;
}
int n, m, cnt;
signed main()
{
while (scanf("%lld", &n) != EOF)
{
memset(tree, 0, sizeof(tree));
printf("Case #%lld:\n", ++cnt);
for (int i = 1; i <= n; ++i)
a[i] = read();
build(1, 1, n);
m = read();
while (m--)
{
char opt = getchar();
if (opt == '0')
{
int x = read(), y = read();
if (x > y)
swap(x, y);
update(x, y, 1, n, 1);
}
else
{
int x = read(), y = read();
if (x > y)
swap(x, y);
printf("%lld\n", query(x, y, 1, n, 1));
}
}
}
return 0;
}
Index 2:
有一个长度为
给出
1:将区间
2:将区间
3:将区间
4:询问区间
多组数据,以 0 0
结尾。
第一行
接下来
Aim 1:次方计算
建三棵线段树树,分别表示
对于乘法,修改操作都十分容易,其中修改时需要把标记归位。
面对加法,需要运用到多项式的乘法。
注意运算顺序,先高次的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 10007;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int sum1[N], sum2[N], sum3[N], add[N], mul[N], alt[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
sum1[p] = (sum1[ls(p)] + sum1[rs(p)]) % Mod;
sum2[p] = (sum2[ls(p)] + sum2[rs(p)]) % Mod;
sum3[p] = (sum3[ls(p)] + sum3[rs(p)]) % Mod;
}
void build(int p, int l, int r)
{
add[p] = alt[p] = 0;
mul[p] = 1;
sum1[p] = sum2[p] = sum3[p] = 0;
if (l == r)
return;
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change_add(int p, int l, int r, int x)
{
x %= Mod;
sum3[p] = (((((sum3[p] % Mod + 3 * sum2[p] * x % Mod) % Mod) + 3 * sum1[p] % Mod * x % Mod * x % Mod)) + (r - l + 1) * x % Mod * x % Mod * x % Mod) % Mod;
sum2[p] = ((sum2[p] % Mod + 2 * x % Mod * sum1[p] % Mod) + (r - l + 1) * x % Mod * x % Mod) % Mod;
sum1[p] = (sum1[p] % Mod + (r - l + 1) * x % Mod) % Mod;
add[p] = (add[p] + x) % Mod;
}
void change_mul(int p, int l, int r, int x)
{
x %= Mod;
sum3[p] = (sum3[p] * x % Mod * x % Mod * x % Mod) % Mod;
sum2[p] = (sum2[p] * x % Mod * x % Mod) % Mod;
sum1[p] = (sum1[p] * x) % Mod;
add[p] = (add[p] * x) % Mod;
mul[p] = (mul[p] * x) % Mod;
}
void change_alt(int p, int l, int r, int x)
{
x %= Mod;
sum3[p] = (r - l + 1) * x % Mod * x % Mod * x % Mod;
sum2[p] = (r - l + 1) * x % Mod * x % Mod;
sum1[p] = (r - l + 1) * x % Mod;
mul[p] = 1;
add[p] = 0;
alt[p] = x;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
if (alt[p])
{
change_alt(ls(p), l, mid, alt[p]);
change_alt(rs(p), mid + 1, r, alt[p]);
alt[p] = 0;
}
if (mul[p] != 1)
{
change_mul(ls(p), l, mid, mul[p]);
change_mul(rs(p), mid + 1, r, mul[p]);
mul[p] = 1;
}
if (add[p])
{
change_add(ls(p), l, mid, add[p]);
change_add(rs(p), mid + 1, r, add[p]);
add[p] = 0;
}
}
void update_add(int nx, int ny, int l, int r, int p, int x)
{
if (nx <= l && r <= ny)
{
change_add(p, l, r, x);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_add(nx, ny, l, mid, ls(p), x);
if (ny > mid)
update_add(nx, ny, mid + 1, r, rs(p), x);
push_up(p);
}
void update_mul(int nx, int ny, int l, int r, int p, int x)
{
if (nx <= l && r <= ny)
{
change_mul(p, l, r, x);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_mul(nx, ny, l, mid, ls(p), x);
if (ny > mid)
update_mul(nx, ny, mid + 1, r, rs(p), x);
push_up(p);
}
void update_alt(int nx, int ny, int l, int r, int p, int x)
{
if (nx <= l && r <= ny)
{
change_alt(p, l, r, x);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_alt(nx, ny, l, mid, ls(p), x);
if (ny > mid)
update_alt(nx, ny, mid + 1, r, rs(p), x);
push_up(p);
}
int query(int nx, int ny, int l, int r, int p, int k)
{
int res = 0;
if (nx <= l && r <= ny)
{
if (k == 1)
return sum1[p];
if (k == 2)
return sum2[p];
if (k == 3)
return sum3[p];
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res = (res + query(nx, ny, l, mid, ls(p), k)) % Mod;
if (ny > mid)
res = (res + query(nx, ny, mid + 1, r, rs(p), k)) % Mod;
return res;
}
signed main()
{
while (scanf("%lld%lld", &n, &m))
{
if (n == 0 && m == 0)
break;
build(1, 1, n);
while (m--)
{
int opt = read(), x = read(), y = read(), c = read();
if (opt == 1)
{
update_add(x, y, 1, n, 1, c);
}
else if (opt == 2)
{
update_mul(x, y, 1, n, 1, c);
}
else if (opt == 3)
{
update_alt(x, y, 1, n, 1, c);
}
else
{
printf("%lld\n", query(x, y, 1, n, 1, c));
}
}
}
return 0;
}
Index 3:
现有
操作分为两种:
- 指定一个区间
,然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开); - 指定一个区间
,要求你输出这个区间内有多少盏灯是打开的。
灯在初始时都是关着的。
第一行有两个整数
接下来有
- 当
的值为 时,表示是第一种操作。 - 当
的值为 时,表示是第二种操作。
对于
Aim 1:维护取反
我们不难发现取反后的贡献会取反。
所以维护
对于标记需要维护这段是否取反。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int tree[N], lazy[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree[p] = tree[ls(p)] + tree[rs(p)];
}
void build(int p, int l, int r)
{
lazy[p] = 0;
if (l == r)
{
tree[p] = 0;
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change(int p, int l, int r)
{
tree[p] = (r - l + 1) - tree[p];
lazy[p] ^= 1;
}
void push_down(int p, int l, int r)
{
if (!lazy[p])
return;
int mid = l + r >> 1;
change(ls(p), l, mid);
change(rs(p), mid + 1, r);
lazy[p] = 0;
}
void update(int nx, int ny, int l, int r, int p)
{
if (nx <= l && r <= ny)
{
tree[p] = (r - l + 1) - tree[p];
lazy[p] ^= 1;
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p));
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p));
push_up(p);
}
int query(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return tree[p];
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query(nx, ny, mid + 1, r, rs(p));
return res;
}
int n, m;
int main()
{
n = read(), m = read();
build(1, 1, n);
while (m--)
{
int opt = read(), l = read(), r = read();
if (opt == 0)
{
update(l, r, 1, n, 1);
}
else
{
printf("%d\n", query(l, r, 1, n, 1));
}
}
return 0;
}
Index 4:
维护一个数列
-
1 l r K D
:给出一个长度等于 的等差数列,首项为 ,公差为 ,并将它对应加到 范围中的每一个数上。即:令 。 -
2 p
:询问序列的第 个数的值 。
第一行两个整数数
第二行
接下来的
若
若
对于
如果您对前面的题毫无压力,那么这道题岂不是非常简单,切一道题目,放松一下!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 5e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N], b[N];
int tree[N], lazy[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree[p] = tree[ls(p)] + tree[rs(p)];
}
void build(int p, int l, int r)
{
lazy[p] = 0;
if (l == r)
{
tree[p] = b[l];
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change(int p, int l, int r, int add)
{
tree[p] += (r - l + 1) * add;
lazy[p] += add;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
change(ls(p), l, mid, lazy[p]);
change(rs(p), mid + 1, r, lazy[p]);
lazy[p] = 0;
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
change(p, l, r, k);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
{
return tree[p];
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query(nx, ny, mid + 1, r, rs(p));
return res;
}
signed main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
a[i] = read(), b[i] = a[i] - a[i - 1];
build(1, 1, n);
while (m--)
{
int opt = read();
if (opt == 1)
{
int l = read(), r = read(), k = read(), d = read();
update(l, l, 1, n, 1, k);
if (l + 1 <= r)
update(l + 1, r, 1, n, 1, d);
if (r + 1 <= n)
update(r + 1, r + 1, 1, n, 1, -(k + d * (r - l)));
}
else
{
int p = read();
printf("%lld\n", query(1, p, 1, n, 1));
}
}
return 0;
}
Index 5:
第一行包含两个正整数
第二行包含
接下来
操作 1 x y k
,表示将第
操作 2 x y
,表示求出第
操作 3 x y
,表示求出第
关于方差:对于一个有
其中
数据点 | 备注 | ||
---|---|---|---|
- | |||
不包含操作 |
|||
- |
Aim 1:定新运算
您有可能是这样的。
方差也太复杂了,线段树做不了!
但是请不要。
现在我带您一起化简这个公式。
Aim 2:lazy标记
tree1 维护和 ; tree2 维护平方和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
#define double long double
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
double tree1[N], tree2[N], lazy[N], a[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree1[p] = tree1[ls(p)] + tree1[rs(p)];
tree2[p] = tree2[ls(p)] + tree2[rs(p)];
}
void build(int p, int l, int r)
{
lazy[p] = 0;
if (l == r)
{
tree1[p] = a[l];
tree2[p] = a[l] * a[l];
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change(int p, int l, int r, double add)
{
tree2[p] += 2 * add * tree1[p] + (r - l + 1) * add * add;
tree1[p] += (r - l + 1) * add;
lazy[p] += add;
}
void push_down(int p, int l, int r)
{
if (!lazy[p])
return;
int mid = l + r >> 1;
change(ls(p), l, mid, lazy[p]);
change(rs(p), mid + 1, r, lazy[p]);
lazy[p] = 0;
}
void update(int nx, int ny, int l, int r, int p, double k)
{
if (nx <= l && r <= ny)
{
change(p, l, r, k);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
double query_add(int nx, int ny, int l, int r, int p)
{
double res = 0;
if (nx <= l && r <= ny)
return tree1[p];
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query_add(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query_add(nx, ny, mid + 1, r, rs(p));
return res;
}
double query_squ(int nx, int ny, int l, int r, int p)
{
double res = 0;
if (nx <= l && r <= ny)
return tree2[p];
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query_squ(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query_squ(nx, ny, mid + 1, r, rs(p));
return res;
}
int n, m;
signed main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
scanf("%Lf", &a[i]);
build(1, 1, n);
while (m--)
{
int opt = read(), l = read(), r = read();
if (opt == 1)
{
double k;
scanf("%Lf", &k);
update(l, r, 1, n, 1, k);
}
else if (opt == 2)
{
printf("%.4Lf\n", query_add(l, r, 1, n, 1) / (r - l + 1));
}
else
{
printf("%.4Lf\n", query_squ(l, r, 1, n, 1) / (r - l + 1) - (query_add(l, r, 1, n, 1) / (r - l + 1)) * (query_add(l, r, 1, n, 1) / (r - l + 1)));
}
}
return 0;
}
Index 6:
维护集合
五种运算如下:
U T
:I T
:D T
:C T
:S T
:
集合的基本运算操作定义如下:
: : : :
输入 (a,b)
,(a,b]
,[a,b)
,[a,b]
表示)。
输出一行若干区间,代表集合
如果区间为空,输出 empty set
。
Aim 1:开闭区间
这样就方便了处理。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 131070 << 2;
const int M = 131070;
const int Mod = 1e9 + 7;
inline void read(int &rl, int &rr)
{
int x = 0, f = 1;
int f1 = 0, f2 = 0;
getchar();
char ch = getchar();
if (ch == '[')
f1 = 1;
ch = getchar();
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
if (f1)
rl = x * 2;
else
rl = x * 2 + 1;
ch = getchar();
x = 0;
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
if (ch == ']')
f2 = 1;
if (f2)
rr = x * 2;
else
rr = x * 2 - 1;
}
int alt[N], rev[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void build(int p, int l, int r)
{
alt[p] = -1;
rev[p] = 0;
if (l == r)
return;
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
}
void change_alt(int p, int l, int r, int k)
{
alt[p] = k;
rev[p] = 0;
}
void change_rev(int p, int l, int r, int k)
{
rev[p] ^= 1;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
if (alt[p] != -1)
{
change_alt(ls(p), l, mid, alt[p]);
change_alt(rs(p), mid + 1, r, alt[p]);
alt[p] = -1;
}
if (rev[p])
{
change_rev(ls(p), l, mid, rev[p]);
change_rev(rs(p), mid + 1, r, rev[p]);
rev[p] = 0;
}
}
void update_alt(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
alt[p] = k;
rev[p] = 0;
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_alt(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update_alt(nx, ny, mid + 1, r, rs(p), k);
}
void update_rev(int nx, int ny, int l, int r, int p)
{
if (nx <= l && r <= ny)
{
rev[p] ^= 1;
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_rev(nx, ny, l, mid, ls(p));
if (ny > mid)
update_rev(nx, ny, mid + 1, r, rs(p));
}
int ans[N];
void init(int p, int l, int r)
{
if (l == r)
{
ans[l] = (alt[p] == -1 ? 0 : alt[p]) ^ rev[p];
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
init(ls(p), l, mid);
init(rs(p), mid + 1, r);
}
int main()
{
build(1, 0, M);
char opt;
while (cin >> opt)
{
int l, r;
read(l, r);
if (l > r)
continue;
if (opt == 'U')
{
update_alt(l, r, 0, M, 1, 1);
}
else if (opt == 'I')
{
update_alt(1, l - 1, 0, M, 1, 0);
update_alt(r + 1, M, 0, M, 1, 0);
}
else if (opt == 'D')
{
update_alt(l, r, 0, M, 1, 0);
}
else if (opt == 'C')
{
update_alt(1, l - 1, 0, M, 1, 0);
update_rev(l, r, 0, M, 1);
update_alt(r + 1, M, 0, M, 1, 0);
}
else
{
update_rev(l, r, 0, M, 1);
}
}
init(1, 0, M);
int flag = 0;
int lft = -1;
for (int i = 0; i <= M; ++i)
{
if (ans[i])
{
if (lft == -1)
lft = i, flag = 1;
if (i == M || !ans[i + 1])
{
if (lft & 1)
printf("(%d,", (lft - 1) / 2);
else
printf("[%d,", lft / 2);
if (i & 1)
printf("%d) ", (i + 1) / 2);
else
printf("%d] ", i / 2);
lft = -1;
}
}
}
if (!flag)
printf("empty set\n");
return 0;
}
Index 7:
你有
你会有
1 A X
从
2 A B
清理
Aim 1:树上二分
运用天然二分性确定插入的右端点,全面覆盖即可。
线段树的基本变形:二分
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int t;
int n, m;
int tree[N];
int alt[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p) { tree[p] = tree[ls(p)] + tree[rs(p)]; }
void build(int p, int l, int r)
{
alt[p] = -1;
tree[p] = 0;
if (l == r)
return;
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
}
void change_alt(int p, int l, int r, int k)
{
alt[p] = k;
tree[p] = (r - l + 1) * k;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
if (alt[p] != -1)
{
change_alt(ls(p), l, mid, alt[p]);
change_alt(rs(p), mid + 1, r, alt[p]);
alt[p] = -1;
}
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
alt[p] = k;
tree[p] = (r - l + 1) * k;
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return tree[p];
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query(nx, ny, mid + 1, r, rs(p));
return res;
}
int sol(int x, int cnt)
{
int l = x, r = n, res = 0;
while (l <= r)
{
int mid = l + r >> 1;
if (mid - x + 1 - query(x, mid, 1, n, 1) >= cnt)
{
res = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
return res;
}
signed main()
{
t = read();
while (t--)
{
n = read(), m = read();
build(1, 1, n);
while (m--)
{
int opt = read();
int A = read(), B = read();
if (opt == 1)
{
++A;
int num = n - A + 1 - query(A, n, 1, n, 1);
if (num == 0)
{
printf("Can not put any one.\n");
continue;
}
int l = sol(A, 1);
int r = sol(A, min(B, num));
update(l, r, 1, n, 1, 1);
if (l <= 0 || r <= 0 || l > r)
{
printf("Can not put any one.\n");
continue;
}
printf("%d %d\n", l - 1, r - 1);
}
else
{
++A, ++B;
printf("%lld\n", query(A, B, 1, n, 1));
update(A, B, 1, n, 1, 0);
}
}
printf("\n");
}
return 0;
}
Index 8:
有一面墙被分为
现在有
第一行为整数
每组数据第一行
接下来
对于
Aim 1:离散数据
我们发现海报数量远远小于方格数量。
便可以离散化节省空间,但需要注意的是如果两个数不相邻,也需要用不相邻的两个数作为它们离散化后的值。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int t, n;
struct node
{
int l, r;
} a[N];
int alt[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void build(int p, int l, int r)
{
alt[p] = -1;
if (l == r)
return;
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
}
void change(int p, int l, int r, int k)
{
alt[p] = k;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
if (alt[p] != -1)
{
change(ls(p), l, mid, alt[p]);
change(rs(p), mid + 1, r, alt[p]);
alt[p] = -1;
}
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
alt[p] = k;
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
}
bool vis[N];
int sol(int p, int l, int r)
{
if (alt[p] != -1)
{
if (vis[alt[p]])
return 0;
vis[alt[p]] = 1;
return 1;
}
if (l == r)
return 0;
push_down(p, l, r);
int mid = l + r >> 1;
return sol(ls(p), l, mid) + sol(rs(p), mid + 1, r);
}
vector<int> uni;
int main()
{
t = read();
while (t--)
{
uni.clear();
uni.push_back(-Mod);
memset(vis, 0, sizeof(vis));
n = read();
int cnt = 0;
for (int i = 1; i <= n; ++i)
a[i].l = read(), a[i].r = read(), uni.push_back(a[i].l), uni.push_back(a[i].r);
sort(uni.begin(), uni.end());
uni.erase(unique(uni.begin(), uni.end()), uni.end());
for (int i = 1; i <= n; ++i)
if (uni[i] - uni[i - 1] != 1)
uni.push_back(uni[i] - 1);
sort(uni.begin(), uni.end());
int m = uni.size() - 1;
build(1, 1, m);
for (int i = 1; i <= n; ++i)
{
update(lower_bound(uni.begin(), uni.end(), a[i].l) - uni.begin(), lower_bound(uni.begin(), uni.end(), a[i].r) - uni.begin(), 1, m, 1, i);
}
printf("%d\n", sol(1, 1, m));
}
return 0;
}
Index 9:
给一个序列
支持 3 种操作
0 u v t
对于所有i1 u v
对于所有i ,输出最大的2 u v
对于所有i ,输出 的和
Aim 1:极值修改
正常打标记,计算严格次大值。
然后正常的打标记
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e6 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int sum[N];
int ma[N], se[N], num[N], a[N], tag[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
sum[p] = sum[ls(p)] + sum[rs(p)];
ma[p] = max(ma[ls(p)], ma[rs(p)]);
if (ma[ls(p)] == ma[rs(p)])
{
se[p] = max(se[ls(p)], se[rs(p)]);
num[p] = num[ls(p)] + num[rs(p)];
}
else
{
se[p] = max(se[ls(p)], se[rs(p)]);
se[p] = max(se[p], min(ma[ls(p)], ma[rs(p)]));
num[p] = ma[ls(p)] > ma[rs(p)] ? num[ls(p)] : num[rs(p)];
}
}
void build(int p, int l, int r)
{
if (l == r)
{
tag[p] = 0;
sum[p] = a[l];
ma[p] = a[l];
se[p] = -1;
num[p] = 1;
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change(int p, int k)
{
if (k >= ma[p])
return;
sum[p] -= num[p] * (ma[p] - k);
ma[p] = k;
tag[p] = 1;
}
void push_down(int p)
{
change(ls(p), ma[p]);
change(rs(p), ma[p]);
tag[p] = 0;
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (k >= ma[p])
return;
if (nx <= l && r <= ny && k > se[p])
{
change(p, k);
return;
}
if (tag[p])
push_down(p);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query_sum(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return sum[p];
if (tag[p])
push_down(p);
int mid = l + r >> 1;
if (nx <= mid)
res += query_sum(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query_sum(nx, ny, mid + 1, r, rs(p));
return res;
}
int query_max(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return ma[p];
if (tag[p])
push_down(p);
int mid = l + r >> 1;
if (nx <= mid)
res = max(res, query_max(nx, ny, l, mid, ls(p)));
if (ny > mid)
res = max(res, query_max(nx, ny, mid + 1, r, rs(p)));
return res;
}
int t, n, m;
signed main()
{
t = read();
while (t--)
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
build(1, 1, n);
while (m--)
{
int opt = read(), l = read(), r = read(), k;
if (opt == 0)
{
k = read();
update(l, r, 1, n, 1, k);
}
else if (opt == 1)
{
printf("%d\n", query_max(l, r, 1, n, 1));
}
else
{
printf("%lld\n", query_sum(l, r, 1, n, 1));
}
}
}
return 0;
}
可持久化线段树
又名主席树,这里讲一个小故事。
这种算法是由HJT(真名暂不给出),正好是中国的一个主席的简称,于是因此得名。
另外一种说法,就是 可持久化() 特别像 主席( )。估计是某个英语学得很好得人,说出的一个梗。
关于这种数据结构是基于线段树的一种新的数据结构,他的大致思想和动画比较相像,计算机为了使得存储的空间更小,只记录了每帧与前一帧的差距,经过类似“减法”的操作。
可持久化线段树正是参考了这样的处理方式,建了很多类似的线段树,为了时间上的优化与空间上的优化,它们会共用一个或多个儿子,这个看似“残缺”的线段树,却可以发挥“完整”的线段树一样的功能。
Index 1:
如题,给定
第一行包含两个整数,分别表示序列的长度
第二行包含
接下来
- 对于
的数据,满足 , , , 。
Aim 1:离散数据
一定要离散化!
这里建的线段树都是值域线段树,但是需要注意的是,对于重复的数据一定要重复建,否则加减的时候会难以计算。
这里离散化的方法默认你应当会了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 2e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int a[N], b[N], root[N];
struct Segment
{
int L, R, sum;
} tree[N << 5];
int cnt = 0;
int update(int l, int r, int pre, int x)
{
int rt = ++cnt;
tree[rt].L = tree[pre].L;
tree[rt].R = tree[pre].R;
tree[rt].sum = tree[pre].sum + 1;
if (l == r)
return rt;
int mid = l + r >> 1;
if (x <= mid)
tree[rt].L = update(l, mid, tree[pre].L, x);
else
tree[rt].R = update(mid + 1, r, tree[pre].R, x);
return rt;
}
int query(int nx, int ny, int l, int r, int k)
{
if (l == r)
return l;
int x = tree[tree[ny].L].sum - tree[tree[nx].L].sum;
int mid = l + r >> 1;
if (x >= k)
return query(tree[nx].L, tree[ny].L, l, mid, k);
else
return query(tree[nx].R, tree[ny].R, mid + 1, r, k - x);
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
a[i] = read(), b[i] = a[i];
sort(b + 1, b + n + 1);
int siz = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i)
{
int it = lower_bound(b + 1, b + siz + 1, a[i]) - b;
root[i] = update(1, siz, root[i - 1], it);
}
while (m--)
{
int l = read(), r = read(), k = read();
printf("%d\n", b[query(root[l - 1], root[r], 1, siz, k)]);
}
return 0;
}
Index 2:
求一个区间内小于等于
和上题一样,只不过增加了二分查询。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int t, n, m;
int a[N], b[N], root[N];
struct Segment
{
int L, R, sum;
} tree[N * 40];
int cnt = 0;
int update(int l, int r, int pre, int x)
{
int rt = ++cnt;
tree[rt].L = tree[pre].L;
tree[rt].R = tree[pre].R;
tree[rt].sum = tree[pre].sum + 1;
if (l == r)
return rt;
int mid = l + r >> 1;
if (x <= mid)
tree[rt].L = update(l, mid, tree[pre].L, x);
else
tree[rt].R = update(mid + 1, r, tree[pre].R, x);
return rt;
}
int siz;
int query(int nx, int ny, int l, int r, int k)
{
if (l == r)
return l;
int mid = l + r >> 1;
int x = tree[tree[ny].L].sum - tree[tree[nx].L].sum;
if (x >= k)
return query(tree[nx].L, tree[ny].L, l, mid, k);
else
return query(tree[nx].R, tree[ny].R, mid + 1, r, k - x);
}
int solve(int x, int y, int k)
{
int l = x, r = y, res = 0;
while (l <= r)
{
int mid = l + r >> 1;
int tmp = b[query(root[x - 1], root[y], 1, siz, mid - x)];
if (tmp <= k)
{
l = mid + 1;
res = max(res, mid - x);
}
else
{
r = mid - 1;
}
}
if (b[query(root[x - 1], root[y], 1, siz, y - x + 1)] <= k)
{
return y - x + 1;
}
return res;
}
int main()
{
int Tcnt = 0;
t = read();
while (t--)
{
cnt = 0;
memset(root, 0, sizeof(root));
for (int i = 1; i <= 40 * n; ++i)
tree[i].L = tree[i].R = tree[i].sum = 0;
n = read(), m = read();
for (int i = 1; i <= n; ++i)
a[i] = read(), b[i] = a[i];
sort(b + 1, b + n + 1);
siz = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i)
{
int it = lower_bound(b + 1, b + siz + 1, a[i]) - b;
root[i] = update(1, siz, root[i - 1], it);
}
printf("Case %d:\n", ++Tcnt);
while (m--)
{
int l = read(), r = read(), k = read();
printf("%d\n", solve(l + 1, r + 1, k));
}
for (int i = 1; i <= 40 * n; ++i)
tree[i].L = tree[i].R = tree[i].sum = 0;
}
return 0;
}
树链剖分
树上的线段树!
Index 1:
一棵树上有
我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t
: 把结点
II. QMAX u v
: 询问从点
III. QSUM u v
: 询问从点
注意:从点
输入文件的第一行为一个整数
接下来
接下来一行
接下来
接下来 CHANGE u t
或者 QMAX u v
或者 QSUM u v
的形式给出。
对于
中途操作中保证每个节点的权值
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
const int Inf = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int t, n;
int top[N], id[N], wt[N], dep[N], fa[N], son[N], siz[N], res;
struct edge
{
int to, next;
} e[N];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void dfs1(int u, int p)
{
dep[u] = dep[p] + 1;
fa[u] = p;
siz[u] = 1;
int maxn = -1;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (v == p)
continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > maxn)
son[u] = v, maxn = siz[v];
}
}
int w[N];
void dfs2(int u, int topf)
{
top[u] = topf;
++res;
id[u] = res;
wt[res] = w[u];
if (!son[u])
return;
dfs2(son[u], topf);
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
int ans[N], tot[N], lazy[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
ans[p] = max(ans[ls(p)], ans[rs(p)]);
tot[p] = tot[ls(p)] + tot[rs(p)];
}
void build(int p, int l, int r)
{
lazy[p] = 0;
if (l == r)
{
ans[p] = wt[l];
tot[p] = wt[l];
return;
}
int mid = (l + r) >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
ans[p] = k;
tot[p] = k;
return;
}
int mid = (l + r) >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query_max(int nx, int ny, int l, int r, int p)
{
int res = -Inf;
if (nx <= l && r <= ny)
return ans[p];
int mid = (l + r) >> 1;
if (nx <= mid)
res = max(res, query_max(nx, ny, l, mid, ls(p)));
if (ny > mid)
res = max(res, query_max(nx, ny, mid + 1, r, rs(p)));
return res;
}
int query_add(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return tot[p];
int mid = (l + r) >> 1;
if (nx <= mid)
res = (res + query_add(nx, ny, l, mid, ls(p)));
if (ny > mid)
res = (res + query_add(nx, ny, mid + 1, r, rs(p)));
return res;
}
int query_range_max(int x, int y)
{
int res = -Inf;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
res = max(res, query_max(id[top[x]], id[x], 1, n, 1));
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
res = max(res, query_max(id[x], id[y], 1, n, 1));
return res;
}
int query_range_add(int x, int y)
{
int res = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
res += query_add(id[top[x]], id[x], 1, n, 1);
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
res += query_add(id[x], id[y], 1, n, 1);
return res;
}
signed main()
{
n = read();
for (int i = 1; i < n; ++i)
{
int u = read(), v = read();
add_edge(u, v);
add_edge(v, u);
}
for (int i = 1; i <= n; ++i)
w[i] = read();
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
int q = read();
while (q--)
{
char s[10];
scanf("%s", s);
if (s[0] == 'C')
{
int x = read(), y = read();
update(id[x], id[x], 1, n, 1, y);
}
else if (s[0] == 'Q' && s[1] == 'M')
{
int x = read(), y = read();
printf("%lld\n", query_range_max(x, y));
}
else
{
int x = read(), y = read();
printf("%lld\n", query_range_add(x, y));
}
}
return 0;
}
Index 2:
爬啊爬,爬啊爬,毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果,“毛景树”上有
Change k w
:将第k条树枝上毛毛果的个数改变为 个。Cover u v w
:将节点 与节点 之间的树枝上毛毛果的个数都改变为 个。Add u v w
:将节点 与节点 之间的树枝上毛毛果的个数都增加 个。
由于毛毛虫很贪,于是他会有如下询问:
Max u v
:询问节点 与节点 之间树枝上毛毛果个数最多有多少个。
第一行一个正整数
接下来 Stop
结束。
对于毛毛虫的每个询问操作,输出一个答案。
对于全部数据,
保证在任意时刻,所有树枝上毛毛果的个数都不会超过
一遍不够,再来一遍,树剖的细节较多一定要多加练习。
Aim 1:边权转换
Q: 这不对啊!!!
A: 咋了?
Q: 你看树剖不是点权吗,这边权怎么弄?
A: 可以把边权挂在儿子上。
Q: 刑。
Q: 别走,不是查询咋搞?
A: 当你爬到一条链的时候我们吧祖先节点扣掉。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int top[N], siz[N], son[N], w[N], id[N], dep[N], fa[N], res;
struct edge
{
int to, dis, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v, int w)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
void dfs1(int u, int p)
{
siz[u] = 1;
dep[u] = dep[p] + 1;
fa[u] = p;
int maxn = -1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p)
continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > maxn)
{
maxn = siz[v], son[u] = v;
}
}
}
void dfs2(int u, int topf, int dist)
{
top[u] = topf;
++res;
id[u] = res;
int tmp = 0;
for (int i = head[u]; i; i = e[i].nxt)
if (e[i].to == son[u])
tmp = e[i].dis;
w[res] = dist;
if (!son[u])
return;
dfs2(son[u], topf, tmp);
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v, e[i].dis);
}
}
int tree1[N], tree2[N];
int add[N], alt[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree1[p] = tree1[ls(p)] + tree1[rs(p)];
tree2[p] = max(tree2[ls(p)], tree2[rs(p)]);
}
void build(int p, int l, int r)
{
add[p] = 0;
alt[p] = -1;
if (l == r)
{
tree1[p] = tree2[p] = w[l];
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change_add(int p, int l, int r, int k)
{
tree1[p] += (r - l + 1) * k;
tree2[p] += k;
add[p] += k;
}
void change_alt(int p, int l, int r, int k)
{
tree1[p] = (r - l + 1) * k;
tree2[p] = k;
alt[p] = k;
add[p] = 0;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
if (alt[p] != -1)
{
change_alt(ls(p), l, mid, alt[p]);
change_alt(rs(p), mid + 1, r, alt[p]);
alt[p] = -1;
}
if (add[p])
{
change_add(ls(p), l, mid, add[p]);
change_add(rs(p), mid + 1, r, add[p]);
add[p] = 0;
}
}
void update_alt(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
change_alt(p, l, r, k);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_alt(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update_alt(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
void update_add(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
change_add(p, l, r, k);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update_add(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update_add(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query_max(int nx, int ny, int l, int r, int p)
{
int res = -Mod;
if (nx <= l && r <= ny)
return tree2[p];
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res = max(res, query_max(nx, ny, l, mid, ls(p)));
if (ny > mid)
res = max(res, query_max(nx, ny, mid + 1, r, rs(p)));
return res;
}
void update_range_alt(int x, int y, int k)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
update_alt(id[top[x]], id[x], 1, n, 1, k);
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
update_alt(id[x] + 1, id[y], 1, n, 1, k);
}
void update_range_add(int x, int y, int k)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
update_add(id[top[x]], id[x], 1, n, 1, k);
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
update_add(id[x] + 1, id[y], 1, n, 1, k);
}
int query_range_max(int x, int y)
{
int res = -Mod;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
res = max(res, query_max(id[top[x]], id[x], 1, n, 1));
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
res = max(res, query_max(id[x] + 1, id[y], 1, n, 1));
return res;
}
struct node
{
int u, v, w;
} a[N];
int main()
{
n = read();
for (int i = 1; i < n; ++i)
{
int u = read(), v = read(), w = read();
add_edge(u, v, w);
add_edge(v, u, w);
a[i].u = u, a[i].v = v, a[i].w = w;
}
dfs1(1, 0);
dfs2(1, 1, 0);
build(1, 1, n);
string opt;
while (1)
{
cin >> opt;
if (opt == "Stop")
break;
int u, v, k;
if (opt == "Change")
{
u = read(), k = read();
update_range_alt(a[u].u, a[u].v, k);
}
else if (opt == "Cover")
{
u = read(), v = read(), k = read();
update_range_alt(u, v, k);
}
else if (opt == "Add")
{
u = read(), v = read(), k = read();
update_range_add(u, v, k);
}
else
{
u = read(), v = read();
printf("%d\n", query_range_max(u, v));
}
}
return 0;
}
Index 3:
Harry Potter 新学了一种魔法:可以让改变树上的果子个数。满心欢喜的他找到了一个巨大的果树,来试验他的新法术。
这棵果树共有
不幸的是,Harry 的法术学得不到位,只能对树上一段路径的节点上的果子个数统一增加一定的数量。也就是说,Harry 的魔法可以这样描述:A u v d
。表示将点
接下来,为了方便检验 Harry 的魔法是否成功,你需要告诉他在释放魔法的过程中的一些有关果树的信息:Q u
。表示当前果树中,以点
第一行一个正整数
接下来
接下来是一个正整数
后面跟着
-
A u v d
,表示将 到 的路径上的所有节点的果子数加上 。保证 -
Q u
,表示询问以 为根的子树中的总果子数,注意是包括 本身的。
对于所有的 Q
操作,依次输出询问的答案,每行一个。答案可能会超过
经过上面地训练,为了增强码力要有一定地练习量。
限时:30min(编码+调试)
要求:一遍通过
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
#define int long long
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
struct node
{
int to, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int top[N], id[N], fa[N], son[N], siz[N], dep[N], res;
void dfs1(int u, int p)
{
dep[u] = dep[p] + 1;
siz[u] = 1;
fa[u] = p;
int maxn = -1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p)
continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > maxn)
maxn = siz[v], son[u] = v;
}
}
void dfs2(int u, int topf)
{
top[u] = topf;
id[u] = ++res;
if (!son[u])
return;
dfs2(son[u], topf);
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
int n, m;
int tree[N], lazy[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree[p] = tree[ls(p)] + tree[rs(p)];
}
void change(int p, int l, int r, int k)
{
lazy[p] += k;
tree[p] += (r - l + 1) * k;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
change(ls(p), l, mid, lazy[p]);
change(rs(p), mid + 1, r, lazy[p]);
lazy[p] = 0;
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
change(p, l, r, k);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return tree[p];
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query(nx, ny, mid + 1, r, rs(p));
return res;
}
void update_range(int x, int y, int k)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
update(id[top[x]], id[x], 1, n, 1, k);
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
update(id[x], id[y], 1, n, 1, k);
}
int query_tree(int x)
{
return query(id[x], id[x] + siz[x] - 1, 1, n, 1);
}
signed main()
{
n = read();
for (int i = 1; i < n; ++i)
{
int u = read() + 1, v = read() + 1;
add_edge(u, v);
add_edge(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
m = read();
while (m--)
{
getchar();
char opt = getchar();
int u, v, d;
if (opt == 'A')
{
u = read() + 1, v = read() + 1, d = read();
update_range(u, v, d);
}
else
{
u = read() + 1;
printf("%lld\n", query_tree(u));
}
}
return 0;
}
Index 4:
给定一棵
- 将节点
到节点 的路径上的所有点(包括 和 )都染成颜色 。 - 询问节点
到节点 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221
由三段组成:11
、222
、1
。
输入的第一行是用空格隔开的两个整数,分别代表树的节点个数
第二行有
第
第
每行首先有一个字符
- 若
为C
,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 ,代表将 到 的路径上所有点都染成颜色 。 - 若
为Q
,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 ,表示查询 到 路径上的颜色段数量。
对于每次查询操作,输出一行一个整数代表答案。
对于 C
或 Q
,保证给出的图是一棵树。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 4e5 + 50;
const int M = 1e5 + 50;
const int Mod = 1e9 + 7;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
int w[N], wt[N], id[N], top[N], dep[N], fa[N], son[N], siz[N], res;
struct edge
{
int to, nxt;
} e[N];
int head[N], cnt;
void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
void dfs1(int u, int p)
{
fa[u] = p;
dep[u] = dep[p] + 1;
siz[u] = 1;
int maxn = -1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == p)
continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > maxn)
son[u] = v, maxn = siz[v];
}
}
void dfs2(int u, int topf)
{
top[u] = topf;
++res;
id[u] = res;
wt[res] = w[u];
if (!son[u])
return;
dfs2(son[u], topf);
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
struct node
{
int l, r, num, lc, rc, alt;
} tree[N];
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void push_up(int p)
{
tree[p].num = tree[ls(p)].num + tree[rs(p)].num;
tree[p].lc = tree[ls(p)].lc, tree[p].rc = tree[rs(p)].rc;
if (tree[ls(p)].rc == tree[rs(p)].lc)
tree[p].num--;
}
void build(int p, int l, int r)
{
tree[p].l = l, tree[p].r = r;
tree[p].alt = -1;
if (l == r)
{
tree[p].lc = tree[p].rc = wt[l];
tree[p].num = 1;
return;
}
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
push_up(p);
}
void change(int p, int l, int r, int k)
{
tree[p].num = 1;
tree[p].lc = tree[p].rc = k;
tree[p].alt = k;
}
void push_down(int p, int l, int r)
{
int mid = l + r >> 1;
if (tree[p].alt != -1)
{
change(ls(p), l, mid, tree[p].alt);
change(rs(p), mid + 1, r, tree[p].alt);
tree[p].alt = -1;
}
}
void update(int nx, int ny, int l, int r, int p, int k)
{
if (nx <= l && r <= ny)
{
change(p, l, r, k);
return;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
update(nx, ny, l, mid, ls(p), k);
if (ny > mid)
update(nx, ny, mid + 1, r, rs(p), k);
push_up(p);
}
int query(int nx, int ny, int l, int r, int p)
{
int res = 0;
if (nx <= l && r <= ny)
return tree[p].num;
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
res += query(nx, ny, l, mid, ls(p));
if (ny > mid)
res += query(nx, ny, mid + 1, r, rs(p));
if (nx <= mid && ny > mid)
{
if (tree[ls(p)].rc == tree[rs(p)].lc)
res--;
}
return res;
}
void update_range(int x, int y, int k)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
update(id[top[x]], id[x], 1, n, 1, k);
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
update(id[x], id[y], 1, n, 1, k);
}
int query_col(int nx, int ny, int l, int r, int p)
{
if (nx <= l && r <= ny)
{
return tree[p].lc;
}
push_down(p, l, r);
int mid = l + r >> 1;
if (nx <= mid)
return query_col(nx, ny, l, mid, ls(p));
if (ny > mid)
return query_col(nx, ny, mid + 1, r, rs(p));
}
int query_range(int x, int y)
{
int res = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
res += query(id[top[x]], id[x], 1, n, 1);
int lc = query_col(id[top[x]], id[top[x]], 1, n, 1);
int rc = query_col(id[fa[top[x]]], id[fa[top[x]]], 1, n, 1);
if (lc == rc)
res--;
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
res += query(id[x], id[y], 1, n, 1);
return res;
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
w[i] = read();
for (int i = 1; i < n; ++i)
{
int u = read(), v = read();
add_edge(u, v);
add_edge(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
while (m--)
{
char opt;
cin >> opt;
if (opt == 'C')
{
int x = read(), y = read(), k = read();
update_range(x, y, k);
}
else
{
int x = read(), y = read();
printf("%d\n", query_range(x, y));
}
}
return 0;
}
矩阵
现代信息的前沿,速度决定一切!
Index 1:
该有向图有
现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?
注意:windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
第一行包含两个整数,分别代表
第
输出一行一个整数代表答案对
对于
Aim 1:邻接矩阵
下面为一个图的邻接矩阵。
平方后。
立方后。
我们发现
Aim 2:一分为九
上面的例子是边权为1。
那么如何转换呢,运用拆点的思想,且十分符合此题的数据。
将一个点拆成九个点,这不就转换成功了吗!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 50;
const int M = 1e5 + 50;
const int Mod = 2009;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, nn, t;
struct Martix
{
int a[110][110];
Martix() { memset(a, 0, sizeof(a)); }
Martix operator*(const Martix &b) const
{
Martix res;
for (int i = 1; i <= nn; ++i)
{
for (int j = 1; j <= nn; ++j)
{
for (int k = 1; k <= nn; ++k)
{
res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % Mod;
}
}
}
return res;
}
} A, B;
void qbow(int k)
{
for (int i = 1; i <= nn; ++i)
B.a[i][i] = 1;
while (k)
{
if (k & 1)
B = B * A;
A = A * A;
k >>= 1;
}
}
signed main()
{
n = read(), t = read();
nn = n * 10;
for (int i = 1; i <= n; ++i)
for (int j = 1; j < 9; ++j)
A.a[10 * (i - 1) + j][10 * (i - 1) + j + 1] = 1;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
int opt;
scanf("%1d", &opt);
A.a[10 * (i - 1) + opt][10 * (j - 1) + 1] = 1;
}
}
qbow(t);
printf("%d\n", B.a[1][n * 10 - 9]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人