【并查集】
int father[5010];
int get(int a)
{
if(father[a]==a)
return a;
else
return father[a]=get(father[a]);
}
void add(int a,int b)
{
a=get(a);
b=get(b);
if(a!=b)
{
father[a]=b; //注意是让b的根节点成为"a的根节点"的根节点
}
}
int main()
{
int n,m,p;
cin>>n;
for(int i=1;i<=n;i++)
father[i]=i; //注意初始化
cin>>m;
cin>>p;
while(m--)
{
int a,b;
cin>>a>>b;
add(a,b);
}
while(p--)
{
int a,b;
cin>>a>>b;
if(get(a)==get(b))
cout<<"Yes";
else
cout<<"No";
if(p!=0)
cout<
【带权并查集】
#include
#include
using namespace std;
int father[30010];
int dist[30010];
int size[30010];
int get(int a)
{
if(father[a]==a)
return a;
int y=father[a];
father[a]=get(y);
dist[a]+=dist[y];
return father[a];
}
void merge(int aa,int bb)
{
int a=get(aa);
int b=get(bb);
if(a!=b)
{
father[a]=b;
dist[a]=size[b];
size[b]+=size[a];
}
}
int main()
{
memset(dist,0,sizeof(dist));
for(int i=0;i<30010;i++)
{
father[i]=i;
size[i]=1; //2个初始化。
}
【bfs】
#include
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
int step[150][150];
struct point
{
int x,y;
point(int xx,int yy)
{
x=xx;
y=yy;
}
};
queue q;
int visit[150][150];
static int n,m;
char b[150][150];
int flag=0;
void bfs(int sx,int sy)
{
q.push(point(sx,sy));
visit[sx][sy]=1;
while(q.empty()!=true)
{
point a=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int xxx=a.x+xx[i];
int yyy=a.y+yy[i];
if(xxx<0 || yyy<0 || xxx>n-1 || yyy>m-1 || b[xxx][yyy]=='#' ||visit[xxx][yyy]==1)
continue;
visit[xxx][yyy]=1;
q.push(point(xxx,yyy));
step[xxx][yyy]=step[a.x][a.y]+1; //最重要,bfs第一次走到的即最短路
if(b[xxx][yyy]=='T')
flag=1;
}
}
}
注意:入栈即访问。
【多重背包】
for(int i=1;i<=种类数;i++)
{
int k=1; //对于每一种,k准备取1 2 4 8...
int temp=m[i]; //m[i]为第i种的数量
for(k; k<=temp ; k*=2)
{
value[++num]=k*v[i]; //v[i]为第i种的每个的价值
temp-=k;
}
if(temp>0)
value[++num]=temp*v[i];
}
这样就可以得到所有种类任意价值数的组合。
多重背包其实就是这样的,先把value数组给弄好了,然后再把value数组的元素每一个都看成一个单独的背包,对这些背包来做01背包的操作即可。
for(int i=0;i<=num;i++)
{
for(long long j=m;j>=value[i];j--)
{
dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
}
}
【LIS最长上升子序列】
【LCS最长公共子序列】
注意算法细节:i和j都是从下标1开始的。所以两个字符数组的输入应该也从下标1开始存。
【状态压缩dp】
https://blog.csdn.net/m0_38033475/article/details/79497154
【枚举子集】
for(int i=t;i;i=(i-1)&t)
{
dp[t]=min(dp[t],dp[i]+dp[i^t]); //这里dp[t]表示选取情况为t二进制时xx操作取得的最小操作数
}
状态压缩dp:
①n很小,不超过20
②组合问题(选取问题——二进制枚举来表示选取情况)
③求最值(直接联想到动态规划)
【gcd求最大公约数】
int gcd(int a,int b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
最小公倍数= a*b/gcd(a,b)
【素数打表】
int f[maxn]; //全局变量,默认为0.素数为0,合数为1
f[1]=1;
for(int i=2;i*i<=n;i++)
{
if(f[i]==0)
{
for(int j=i*i;j<=n;j+=i)
{
f[j]=1;
}
}
}
【欧拉公式求互质数个数】(求有多少个i (i属于1~n) 使得 gcd(i ,n)==1 )
求很多次,打表法:https://blog.csdn.net/m0_38033475/article/details/79510301
只求几次,直接用公式 phi(n)=n*(1-1/p1)*(1-1/p2)...*(1-1/pi) p是它的质因子(不重复)
【拓展欧几里得】
扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式: ax+by = gcd(a, b) =d
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
long long r=gcd1(b,a%b,x,y);
long long t=x;
x=y;
y=t-a/b*y;
return r;
}
【二分快速幂】
int pow_mod(int a,int b,int mod)
{
if(b==0)
return 1%mod;
int temp=pow_mod(a,b/2,mod);
temp=temp*temp%mod;
if(b%2==1)
temp=temp*a%mod;
return temp;
}
【矩阵二分快速幂 优化dp】
int n; // 所有矩阵都是 n * n 的矩阵
struct matrix {
int a[100][100]; //这个维度倒无所谓,根据实际维度定也可以,不会这么大的 - - 因为是你手写推出来的矩阵。
};
matrix matrix_mul(matrix A, matrix B, int mod) {
// 2 个矩阵相乘
matrix C;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
C.a[i][j] = 0;
for (int k = 0; k < n; ++k) {
C.a[i][j] += A.a[i][k] * B.a[k][j] % mod;
C.a[i][j] %= mod;
}
}
}
return C;
}
matrix unit() {
// 返回一个单位矩阵
matrix res;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i == j) {
res.a[i][j] = 1;
} else {
res.a[i][j] = 0;
}
}
}
return res;
}
matrix matrix_pow(matrix A, int n, int mod) {
// 快速求矩阵 A 的 n 次方
matrix res = unit(), temp = A;
for (; n; n /= 2) { //或者写 n>>=1 注意要加等号!
if (n & 1) {
res = matrix_mul(res, temp, mod);
}
temp = matrix_mul(temp, temp, mod);
}
return res;
}
【二分图匹配】
#include
#include
using namespace std;
int line[2001][2001];
int used[2001];
int boy[2001];
int n;
int find(int x) //第x个女生
{
for(int i=1;i<=n;i++)//遍历男生
{
if(line[x][i]==1 && used[i]==0) //x女喜欢i男,且i男没被选
{
used[i]=1; //x女生要选i男
if(boy[i]==0 || find(boy[i])) //如果i男单身 or i男名草有主能甩掉前女友(前女友能去递归找到新男友)
{
boy[i]=x; //那么i男就跟x女走
return 1; //提示x女找到男朋友了
}
}
}
return 0; //x女只能落单
}
int main()
{
cin>>n;
int k,t;
for(int i=1;i<=n;i++)
{
cin>>k;
while(k--)
{
cin>>t;
line[i][t]=1;
}
}
int cnt=0;
for(int x=1;x<=n;x++) //女的来挑男的啦!
{
memset(used,0,sizeof(used)); //关键!表示针对每个女的,所有男的都还没被选过。记住每次都要清零。
if(find(x)) cnt++;
}
cout<
【差分法+前缀和】
https://blog.csdn.net/m0_38033475/article/details/79758621
【巧用excel日期题】
打开Excel,先把格式调成“日期”,然后就可以加减运算啦!超级爽!(不过求星期几还是得记蔡什么姆的公式)
【尺取法】
https://blog.csdn.net/m0_38033475/article/details/79897291
【贪心】
https://blog.csdn.net/m0_38033475/article/details/79678953
https://blog.csdn.net/m0_38033475/article/details/79760961
https://blog.csdn.net/m0_38033475/article/details/79949081
【博弈】
https://blog.csdn.net/m0_38033475/article/details/79903888
【费马小定理】
https://blog.csdn.net/m0_38033475/article/details/79925115
【diijkstra】
const int MAX_N = 10000;
const int MAX_M = 100000;
const int inf = 0x3f3f3f3f;
struct edge {
int v, w, next;
} e[MAX_M];
int p[MAX_N], eid, n;
void mapinit() {
memset(p, -1, sizeof(p));
eid = 0;
}
void insert(int u, int v, int w) { // 插入带权有向边
e[eid].v = v;
e[eid].w = w;
e[eid].next = p[u];
p[u] = eid++;
}
void insert2(int u, int v, int w) { // 插入带权双向边
insert(u, v, w);
insert(v, u, w);
}
typedef pair PII;
set > min_heap;
/*用 set 来伪实现一个小根堆,并具有映射二叉堆的功能。堆中 pair 的 second 表示顶点下标,first 表示该顶点的 dist 值,注意,只要写上less就会一first为标准排序,C++,内部实现,不需要管*/
int dist[MAX_N]; // 存储单源最短路的结果
bool vst[MAX_N]; // 标记每个顶点是否在集合 U 中
bool dijkstra(int s) {
// 初始化 dist、小根堆和集合 U
memset(vst, 0, sizeof(vst));
memset(dist, 0x3f, sizeof(dist));
min_heap.insert(make_pair(0, s));
dist[s] = 0;
for (int i = 0; i < n; ++i) {
if (min_heap.size() == 0) { // 如果小根堆中没有可用顶点,说明有顶点无法从源点到达,算法结束
return false;
}
// 获取堆顶元素,并将堆顶元素从堆中删除
auto iter = min_heap.begin(); //auto的“指针!”数据类型都用上了,为C++11打call!
int v = iter->second;
min_heap.erase(*iter);
vst[v] = true;
// 进行和普通 dijkstra 算法类似的松弛操作
for (int j = p[v]; j != -1; j = e[j].next) {
int x = e[j].v;
if (!vst[x] && dist[v] + e[j].w < dist[x]) { //对未确定的估计值进行更新。
// 先将对应的 pair 从堆中删除,再将更新后的 pair 插入堆
min_heap.erase(make_pair(dist[x], x));
dist[x] = dist[v] + e[j].w;
min_heap.insert(make_pair(dist[x], x));
}
}
}
return true; // 存储单源最短路的结果
}
【SPFA】
算法流程:
使用di表示从源点到顶点i的最短路,额外用一个队列来保存即将进行拓展的顶点列表,并用inqi来标识顶点i是不是在队列中。
- 初始队列中仅包含源点,且源点s的ds=0 。
- 取出队列头顶点u,扫描从顶点u出发的每条边,设每条边的另一端为v,边
bool inq[MAX_N]; //判断是否在队列中
int d[MAX_N]; // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
void spfa(int s) {
memset(inq, 0, sizeof(inq)); //初始化ing,d
memset(d, 0x3f, sizeof(d));
d[s] = 0; //原点的距离为0,s入队
inq[s] = true;
queue q;
q.push(s);
while (!q.empty()) { //不空时
int u = q.front();
q.pop();
inq[u] = false; //出队还要处理一下!
for (int i = p[u]; i != -1; i = e[i].next) {//遍历,和以前一样
int v = e[i].v;
if (d[u] + e[i].w < d[v]) {//更近了
d[v] = d[u] + e[i].w;
if (!inq[v]) { //不在队内
q.push(v);
inq[v] = true;
}
}
}
}
}
【差分约束系统——spfa应用】
https://blog.csdn.net/m0_38033475/article/details/80015141
【floyd】
const int inf = 0x3f3f3f3f;
int g[MAX_N][MAX_N]; // 算法中的 G 矩阵
// 初始化 g 矩阵
void init() {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i == j) {
g[i][j] = 0;
} else {
g[i][j] = inf;
}
}
}
}
// 插入一条带权有向边
void insert(int u, int v, int w) {
g[u][v] = w;
}
// 核心代码
void floyd() {
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (g[i][k] + g[k][j] < g[i][j]) {
g[i][j] = g[i][k] + g[k][j];
}
}
}
}
}
【kruskal最小生成树】
#include
#include
using namespace std;
const int MAXN=10000;
const int MAXM=10000;
struct edge
{
int u,v,w;
}e[MAXM];
bool cmp(edge a,edge b) //重要!
{
if(a.w>n>>m;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e+1,e+m+1,cmp); //把边按边权排序!
for(int i=1;i<=n;i++)
father[i]=i;
int res=0; //记录添加到最小生成树中的边数(最终=n-1即可 !!!
int ans=0; //记录边权值和
for(int i=1;i<=m && res
【LCA最近公共祖先】
#include
#include
#include
using namespace std;
const int MAX_N=100000;
const int MAX_M=1000000;
int isleave[100050];
struct edge{
int v,next;
}E[MAX_M];
int p[MAX_N],eid;
void init(){
memset(p,-1,sizeof(p));
memset(isleave,0,sizeof(isleave));
eid=0;
}
void insert(int u,int v){
E[eid].v=v;
E[eid].next=p[u];
p[u]=eid++;
}
int d[MAX_N],fa[MAX_N][20];
void dfs(int u){
for(int i=p[u];i!=-1;i=E[i].next){
if(d[E[i].v]==-1){
d[E[i].v]=d[u]+1;
fa[E[i].v][0]=u;
dfs(E[i].v);
}
}
}
int lca(int x,int y){
int i,j;
if(d[x]=0;j--){
if(d[x]-(1<=d[y]){
x=fa[x][j];
}
}
if(x==y){
return x;
}
for( j=i;j>=0;j--)
{
if(fa[x][j]!=fa[y][j]){
x=fa[x][j];
y=fa[y][j];
}
}
return fa[x][0];
}
int main() {
int n;
init();
cin>>n;
for(int i=0;i>u>>v;
insert(u,v);
insert(v,u);
isleave[v]=1;
}
memset(d,-1,sizeof(d));
int root;
for(int i=1;i<=n;i++)
{
if(isleave[i]==0){
root=i;
break;
}
}
d[root]=1;
dfs(root);
for(int level=1;(1<>q;
while(q--){
int a,b;
cin>>a>>b;
cout<
【拓扑排序】
就是要用链式前向星、队列、一个需要根据输入赋值的indegree[MAX_N]数组记录每个节点的入度
struct edge {
int v, next;
} e[MAX_M];
int p[MAX_N], eid;
int topo() {
queue q;
for (int i = 1; i <= n; i++) {
if (indegree[i] == 0) { // 将所有入度为零的顶点入队
q.push(i);
}
}
while (!q.empty()) {
int now = q.front();
cout << "visiting " << now << endl;
q.pop();
for (int i = p[now]; i != -1; i = e[i].next) {
int v = e[i].v;
indegree[v]--;
if (indegree[v] == 0) { // 将入度新变成零的顶点入队
q.push(v);
}
}
}
}
【欧拉回路】
#include
#include
using namespace std;
const int MAXN=1010;
vector v[MAXN];
int N,M;
int cnt=0;
void olahuilu(int s)
{
for(auto t=v[s].begin();t!=v[s].end();t=v[s].begin())
{
int a=*t;
v[s].erase(t);
v[a].erase(find(v[a].begin(),v[a].end(),s));
cnt++;
olahuilu(a);
}
//road[count++]=s;
}
int main()
{
cin>>N>>M;
int temp=M;
while(M--)
{
int a,b;
cin>>a>>b;
v[a].push_back(b);
v[b].push_back(a);
}
for(int i=1;i<=N;i++)
{
if(v[i].size()%2!=0)
{
cout<<0;
return 0;
}
}
olahuilu(1); //因为无向图找欧拉回路,所以从任意一个起点开始即可
if(temp==cnt) cout<<1;
else cout<<0;
return 0;
}
【tarjan求最小强连通分量的点个数】
#include
#include
using namespace std;
const int maxn=2e5+5;
vector v[maxn];
int dfn[maxn];
int low[maxn];
int cnt=0;
int vis[maxn]; //1表示在栈中
stack s;
int minn=0x3f3f3f3f;
void tarjan(int a)
{
s.push(a);
vis[a]=1;
dfn[a]=low[a]=++cnt;
for(int i=0;i>n;
for(int i=1;i<=n;i++)
{
int e;
scanf("%d",&e);
v[i].push_back(e);
}
tarjan(1);
cout<
【tarjan缩点】
#include
#include
using namespace std;
const int maxn=1e5;
vector v[maxn];
int superpoint[maxn];
stack s;
int dfn[maxn],low[maxn];
int vis[maxn];
int cnt=0;
int sum=0;
void tarjan(int a)
{
vis[a]=1;
dfn[a]=low[a]=++cnt;
s.push(a);
for(int i=0;i>n>>m;
while(m--)
{
int a,b;
scanf("%d %d",&a,&b);
v[a].push_back(b);
}
tarjan(1);
for(int i=1;i<=n;i++)
{
if(dfn[i]==0)
tarjan(i); //这步要记住!因为不一定所有都能由tanrjan(1)可达,有可能有几个点是孤立的小团体~
}
for(int i=1;i<=n;i++)
{
for(int j=0;j1)
cout<<0;
else if(temp==1)
cout<
【dicnic】
const int MAX_N = 100; // X 集合中的顶点数上限
const int MAX_M = 10000; // 总的边数上限
struct edge {
int v, c, next; // v 是指边的另一个顶点,c 表示容量
} e[MAX_M];
int p[MAX_N], eid;
void init() {
memset(p, -1, sizeof(p));
eid = 0;
}
void insert(int u, int v, int c) { // 插入一条从 u 连向 v,容量为 c 的弧
e[eid].v = v;
e[eid].c = c;
e[eid].next = p[u];
p[u] = eid++;
}
void addedge(int u, int v, int c) { // 用 insert2 来插入网络中的弧
insert(u, v, c);
insert(v, u, 0); // 插入一条方向相反、当前容量为 0 的弧
}
int S, T; // S 是源点,T 是汇点
int d[MAX_N]; // 存储每个顶点的层次
bool bfs() {
memset(d, -1, sizeof(d));
queue q;
q.push(S);
d[S] = 0;
while (q.empty()==false) {
int u = q.front();
q.pop();
for (int i = p[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if (e[i].c > 0 && d[v] == -1) {
q.push(v);
d[v] = d[u] + 1;
}
}
}
return (d[T] != -1);
}
int dfs(int u, int flow) { // flow 表示当前搜索分支的流量上限
if (u == T) {
return flow;
}
int res = 0;
for (int i = p[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if (e[i].c > 0 && d[u] + 1 == d[v]) {
int tmp = dfs(v, min(flow, e[i].c)); // 递归计算顶点 v,用 c(u, v) 来更新当前流量上限
flow -= tmp;
e[i].c -= tmp;
res += tmp;
e[i ^ 1].c += tmp; // 修改反向弧的容量
if (flow == 0) { // 流量达到上限,不必继续搜索了
break;
}
}
}
if (res == 0) { // 当前没有经过顶点 u 的可行流,不再搜索顶点 u
d[u] = -1;
}
return res;
}
int maxflow() { // 函数返回值就是最大流的结果
int res = 0;
while (bfs()) {
res += dfs(S, INF); // 初始流量上限为 INF
}
return res;
}
【博弈论】
SG函数 https://blog.csdn.net/m0_38033475/article/details/80272171
SG小结 https://blog.csdn.net/m0_38033475/article/details/80292465
【KMP】
void getnext() {
next[1] = 0; //这里默认字符串都从下标1开始哈!
for(int i = 2; i <= n; i++) { //i从2开始
int j = next[i - 1];
while(t[j + 1] != t[i] && j > 0) {
j = next[j];
}
if(t[j + 1] == t[i]) {
next[i] = j + 1;
} else {
next[i] = 0;
}
}
}
int kmp() {
int j = 0;
for(int i = 1; i <= m; i++){
while(t[j + 1] != s[i] && j > 0) { //如果不匹配则一直去赋为next[j]直到匹配或无法匹配
j = next[j];
}
if(t[j + 1] == s[i]) {
j++;
}
if(j >= n) {
return i - n + 1; //第一个匹配的起点
}
}
return 0;
}
【拓展KMP】
void getnext() {
next[1] = n;
int p = 1;
while(p < n && t[p] == t[p + 1]) p++;
next[2] = p-1; //对本身的比较来说,先可以把next[1]和next[2]都赋好(求next[2]相当于把字符串向右移一位来比)
int k = 2,l;
for(int i = 3; i <= n; i++) {
p = k + next[k] - 1;
l = next[i - k + 1];
if (i + l <= p) next[i] = l;
else {
int j = p - i + 1;
if(j < 0) j = 0;
while(i + j <= n && t[i + j] == t[j + 1]) j++;
next[i] = j;
k = i;
}
}
}
void getextend() {
int p = 0;
while (p < m && p < n && s[p + 1] == t[p + 1]) {
p++;
}
extend[1] = p;
int k = 1, l;
for (int i = 2; i <= m; i++) {
p = k + extend[k] - 1;
l = next[i - k + 1];
if (i + l <= p) {
extend[i] = l;
} else {
int j = p - i + 1;
if(j < 0) j = 0;
while(i + j <= m && j + 1 <=n && s[i + j] == t[j + 1]) j ++;
extend[i] = j;
k = i;
}
}
}
【字典树】
#include
#include
using namespace std;
const int maxn=566666;
int s[maxn][26];
bool e[maxn];
int tot=0;
void init()
{
memset(s,-1,sizeof(s));
memset(e,false,sizeof(e));
}
bool insert(char *t,int len) //传入的字符串和其长度
{
int temp=tot;
int p=0;
for(int i=0;i>n;
bool flag=true;
while(n--)
{
char t[12];
scanf("%s",t);
if(flag)
flag=insert(t,strlen(t));
}
if(flag==true)
cout<<"Good Luck!";
else
cout<<"Bug!";
return 0;
}
【树状数组】
int sum(int i)
{
int res=0;
while(i>0)
{
res+=c[i];
i-=i&(-i);
}
return res;
}
void add(int i,int v)
{
while(i<=n)
{
c[i]+=v;
i+=i&(-i);
}
}
【二维树状数组】
【树状数组维护区间最值】
【线段树】
线段树存储区间最大值模板题代码
#include
#include
using namespace std;
const int maxn=8e5+5; //注意数组开四倍哦!!!(题中写的:n<=200000)
int s[maxn];
int renew(int p,int l,int r,int x,int v) //单点修改。注意s保存的是最大值
{
if(l==r) return s[p]=v;
int mid=(l+r)/2;
if(x<=mid) return s[p]=max(s[p*2+1],renew(p*2,l,mid,x,v));
else if(x>mid) return s[p]=max(s[p*2],renew(p*2+1,mid+1,r,x,v));
}
int getmax(int p,int l,int r,int x,int y)
{
if(x<=l && y>=r) return s[p];
int mid=(l+r)/2;
int m=0;
if(x<=mid) m=max(m,getmax(p*2,l,mid,x,y));
if(y>mid) m=max(m,getmax(p*2+1,mid+1,r,x,y));
return m;
}
int main()
{
int n,m;
char a;
int b,c;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&b);
renew(1,1,n,i,b);
}
for(int i=0;i
【线段树区间更新】
求总价值 代码
#include
#include
using namespace std;
const int maxn=4e5+10;
int s[maxn];
int col[maxn]; //保存lazy值
void down(int p,int l,int r)
{
if(col[p]!=0)
{
int mid=(l+r)/2;
s[p*2]=col[p]*(mid-l+1);
s[p*2+1]=col[p]*(r-mid);
col[p*2]=col[p*2+1]=col[p];
col[p]=0;
}
}
void up(int p)
{
s[p]=s[p*2]+s[p*2+1];
}
void renew(int p,int l,int r,int x,int y,int v)
{
if(x<=l&&y>=r)
{
s[p]=(r-l+1)*v;
col[p]=v;
return;
}
down(p,l,r);
int mid=(l+r)/2;
if(x<=mid) renew(p*2,l,mid,x,y,v);
if(y>mid) renew(p*2+1,mid+1,r,x,y,v);
up(p);
}
/*
int getsum(int p,int l,int r,int x,int y)
{
if(x<=l && y>=r)
return s[p];
int res=0;
int mid=(l+r)/2;
if(x<=mid) res+=getsum(p*2,l,mid,x,y);
if(y>mid) res+=getsum(p*2+1,mid+1,r,x,y);
return res;
} */
int main()
{
int n,q;
cin>>n>>q;
renew(1,1,n,1,n,1);
int x,y,z;
while(q--)
{
scanf("%d%d%d",&x,&y,&z);
renew(1,1,n,x,y,z);
}
printf("The total value of the hook is %d.\n",s[1]); //蠢了。。
return 0;
}
加油!