读入输出 128 数据
inline __int128 read(){
__int128 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 * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
__int128 2^128
线段树 (区间查询都是求和)
线段树单点修改,区间查询
#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=5000005;
int n,t,w[N];
struct node
{
int l,r,sum;
}tr[N*4];
void build(int p,int l,int r)
{
tr[p]={l,r,w[l]};
if(l==r) return ;
int m= (l+r)>>1;
build(lc,l,m);
build(rc,m+1,r);
tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void update(int p,int x,int k)
{
if(tr[p].l==x&&tr[p].r==x)
{
tr[p].sum+=k;return ;
}
int m=(tr[p].l+tr[p].r)>>1;
if(x<=m) update(lc,x,k);
if(x>m) update(rc,x,k);
tr[p].sum=tr[lc].sum+tr[rc].sum;
}
int query(int p,int x,int y)
{
if(x<=tr[p].l&&tr[p].r<=y)
return tr[p].sum;
int m=(tr[p].l+tr[p].r)>>1;
int sum=0;
if(x<=m) sum+=query(lc,x,y);
if(y>m) sum+=query(rc,x,y);
return sum;
}
区间修改 区间查询 (修改操作只有加法)
#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=5000005;
int n,q,w[N];
struct node
{
int l,r,sum,add;
}tr[N*4];
void pushup(int p)
{
tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int p)
{
if(tr[p].add)
{
tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1);
tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1);
tr[lc].add+=tr[p].add,
tr[rc].add+=tr[p].add,
tr[p].add=0;
}
}
void build(int p,int l,int r)
{
//cout<<l<<" "<<r<<endl;
tr[p]={l,r,w[l],0};
if(l==r) return ;
int m=(l+r)>>1;
build(lc,l,m);
build(rc,m+1,r);
pushup(p);
}
void update(int p,int x,int y,int k)
{
if(x<=tr[p].l&&tr[p].r<=y)
{
tr[p].sum+=(tr[p].r-tr[p].l+1)*k;
tr[p].add+=k;
return ;
}
int m=(tr[p].l+tr[p].r)>>1;
pushdown(p);
if(x<=m) update(lc,x,y,k);
if(y>m) update(rc,x,y,k);
pushup(p);
}
int query(int p,int x,int y)
{
//cout<<"fFdsf:"<<tr[p].l<<" "<<tr[p].r<<endl;
if(x<=tr[p].l&&tr[p].r<=y)
return tr[p].sum;
int m=(tr[p].l+tr[p].r)>>1;
//cout<<"m:"<<m<<endl;
pushdown(p);
int sum=0;
//cout<<":"<<x<<" "<<y<<" "<<m<<endl;
if(x<=m) sum+=query(lc,x,y);
if(y>m) sum+=query(rc,x,y);
return sum;
}
区间修改 区间查询 (修改操作有乘法有加法)
#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=2e5+5;
const int mod = 1e9 + 7;
int n,q,w[N] ;
char s[N];
struct node
{
int l,r,sum,mul,add;
}tr[N*4];
void pushup(int p)
{
tr[p].sum = (tr[lc].sum + tr[rc].sum)%mod ;
}
void pushdown(int p)
{
if( tr[p].mul != 1)
{
tr[lc].sum *= tr[p].mul; tr[lc].sum %= mod;
tr[rc].sum *= tr[p].mul; tr[rc].sum %= mod;
tr[lc].mul *= tr[p].mul; tr[lc].mul %= mod;
tr[rc].mul *= tr[p].mul; tr[rc].mul %= mod;
tr[lc].add *= tr[p].mul; tr[lc].add %= mod;
tr[rc].add *= tr[p].mul; tr[rc].add %= mod;
tr[p].mul = 1;
}
if( tr[p].add != 0 )
{
tr[lc].sum += tr[p].add*( tr[lc].r - tr[lc].l + 1 )%mod; tr[lc].sum %= mod;
tr[rc].sum += tr[p].add*( tr[rc].r - tr[rc].l + 1 )%mod; tr[rc].sum %= mod;
tr[lc].add += tr[p].add; tr[lc].add %= mod;
tr[rc].add += tr[p].add; tr[rc].add %= mod;
tr[p].add = 0;
}
}
void build(int p,int l,int r)
{
tr[p]={l,r,w[l],1,0};
if(l==r) return ;
int m=(l+r)>>1;
build(lc,l,m);
build(rc,m+1,r);
pushup(p);
}
void update_add(int p,int x,int y,int k)
{
if(x<=tr[p].l&&tr[p].r<=y)
{
tr[p].sum += k*(tr[p].r - tr[p].l + 1)%mod; tr[p].sum %= mod;
tr[p].add += k; tr[p].add %= mod;
return ;
}
int m=(tr[p].l+tr[p].r)>>1;
pushdown(p);
if(x<=m) update_add(lc,x,y,k);
if(y>m) update_add(rc,x,y,k);
pushup(p);
}
void update_mul(int p,int x,int y,int k)
{
if(x<=tr[p].l&&tr[p].r<=y)
{
tr[p].sum *= k; tr[p].sum %= mod;
tr[p].mul *= k; tr[p].mul %= mod;
tr[p].add *= k; tr[p].add %= mod;
return ;
}
int m=(tr[p].l+tr[p].r)>>1;
pushdown(p);
if(x<=m) update_mul(lc,x,y,k);
if(y>m) update_mul(rc,x,y,k);
pushup(p);
}
int query(int p,int x,int y)
{
if(x<=tr[p].l&&tr[p].r<=y)
return tr[p].sum;
int m=(tr[p].l+tr[p].r)>>1;
//cout<<"m:"<<m<<endl;
pushdown(p);
int res=0;
//cout<<":"<<x<<" "<<y<<" "<<m<<endl;
if(x<=m) res += query(lc,x,y),res %= mod;
if(y>m) res += query(rc,x,y),res %= mod;
pushup(p);
return res;
}
void solve()
{
cin>>n>>q>>mod;
for(int i=1;i<=n;i++)
cin>>w[i];
build(1,1,n);
while(q--)
{
int op;
cin>>op;
if(op==1) // 乘法
{
int x,y,k;
cin>>x>>y>>k;
update_mul(1,x,y,k);//f();
}
else if(op==2) // 加法
{
int x,y,k;
cin>>x>>y>>k;
update_add(1,x,y,k);//f();
}
else
{
int x,y;
cin>>x>>y;
cout<< query(1,x,y) <<endl;
}
}
}
signed main()
{
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
solve();
return 0;
}
/*
8 10 1000212
1 2 2 3 4 6 7 8
1 2 5 3
*/
树链剖分
//lugo 3384
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5;
int w[N];
vector<int> e[N];
int fa[N] , dep[N] , sz[N] , son[N];
int top[N],id[N],nw[N],tim; //top[i]:i点的链的最上方 id[i],i点新的编号,nw[i]:i点的值
//区间修改 区间查询 (修改操作只有加法)
#define lc p<<1
#define rc p<<1|1
int n,q,r,mod;
struct node
{
int l,r,sum,add;
}tr[N*4];
void pushup(int p)
{
tr[p].sum=tr[lc].sum+tr[rc].sum; tr[p].sum %= mod;
}
void pushdown(int p)
{
if(tr[p].add)
{
tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1); tr[lc].sum %= mod;
tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1); tr[rc].sum %= mod;
tr[lc].add+=tr[p].add, tr[lc].add %= mod;
tr[rc].add+=tr[p].add, tr[rc].add %= mod;
tr[p].add=0;
}
}
void build(int p,int l,int r)
{
//cout<<l<<" "<<r<<endl;
tr[p]={l,r,nw[l],0};
if(l==r) return ;
int m=(l+r)>>1;
build(lc,l,m);
build(rc,m+1,r);
pushup(p);
}
void update(int p,int x,int y,int k)
{
if(x<=tr[p].l&&tr[p].r<=y)
{
tr[p].sum+=(tr[p].r-tr[p].l+1)*k%mod; tr[p].sum %= mod;
tr[p].add+=k; tr[p].add %= mod;
return ;
}
int m=(tr[p].l+tr[p].r)>>1;
pushdown(p);
if(x<=m) update(lc,x,y,k);
if(y>m) update(rc,x,y,k);
pushup(p);
}
int query(int p,int x,int y)
{
//cout<<"fFdsf:"<<tr[p].l<<" "<<tr[p].r<<endl;
if(x<=tr[p].l&&tr[p].r<=y)
return tr[p].sum%mod;
int m=(tr[p].l+tr[p].r)>>1;
//cout<<"m:"<<m<<endl;
pushdown(p);
int sum=0;
//cout<<":"<<x<<" "<<y<<" "<<m<<endl;
if(x<=m) sum+=query(lc,x,y) , sum %= mod;
if(y>m) sum+=query(rc,x,y) , sum %= mod;
return sum;
}
//更新数组 fa , dep , sz , son
void dfs1(int u,int father)
{
fa[u] = father, dep[u] = dep[father]+1,sz[u] = 1;
for(auto v:e[u] )
{
if(v==father) continue;
dfs1(v,u);
sz[u] += sz[v];
if( sz[son[u]] < sz[v] ) son[u] = v;
}
}
/*
把树分成多个链条,让树变成线性,好让树做线段树操作
根到任意点的重链最多有logn条
每条链条的开头都是父节点的轻儿子,除了第一个链条(头为根节点)
*/
void dfs2(int u,int t)
{
top[u] = t;id[u]=++tim;nw[tim]=w[u];
if(!son[u]) return ;
dfs2(son[u],t);
for(auto v:e[u])
{
if( v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
/*
通过链条跳到 lca 点
途径的链条不超过logn条 , 每根链条,它们的索引都是连续的,因为最开始用时间戳来建立的链条
*/
void update_path(int u,int v,int k)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,id[top[u]],id[u],k);
u = fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
update(1,id[v],id[u],k);
}
int query_path(int u,int v)
{
int res = 0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res += query(1,id[top[u]],id[u]); res %= mod;
u = fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
res += query(1,id[v],id[u]); res %= mod;
return res;
}
void update_xia(int u,int k)
{
update(1,id[u],id[u]+sz[u]-1,k);
}
int query_xia(int u)
{
return query(1,id[u],id[u]+sz[u]-1)%mod;
}
signed main()
{
cin>>n>>q>>r>>mod;
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(r,0);
dfs2(r,r);
build(1,1,n);
int x,y,z;
while(q--)
{
int op;
cin>>op;
if(op==1)
{
cin>>x>>y>>z;
update_path(x,y,z);
}
else if(op==2)
{
cin>>x>>y;
cout<<query_path(x,y)<<endl;
}
else if(op==3)
{
cin>>x>>z;
update_xia(x,z);
}
else
{
cin>>x;
cout<<query_xia(x)<<endl;
}
}
return 0;
}
AC自动机
给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
struct Tree//字典树
{
int fail;//失配指针
int vis[26];//子节点的位置
int end;//标记有几个单词以这个节点结尾
}AC[1000000];//Trie树
int cnt=0;//Trie的指针
inline void Build(string s)
{
int l=s.length();
int now=0;//字典树的当前指针
for(int i=0;i<l;++i)//构造Trie树
{
if(AC[now].vis[s[i]-'a']==0)//Trie树没有这个子节点
AC[now].vis[s[i]-'a']=++cnt;//构造出来
now=AC[now].vis[s[i]-'a'];//向下构造
}
AC[now].end+=1;//标记单词结尾
}
void Get_fail()//构造fail指针
{
queue<int> Q;//队列
for(int i=0;i<26;++i)//第二层的fail指针提前处理一下
{
if(AC[0].vis[i]!=0)
{
AC[AC[0].vis[i]].fail=0;//指向根节点
Q.push(AC[0].vis[i]);//压入队列
}
}
while(!Q.empty())//BFS求fail指针
{
int u=Q.front();
Q.pop();
for(int i=0;i<26;++i)//枚举所有子节点
{
if(AC[u].vis[i]!=0)//存在这个子节点
{
AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i];
//子节点的fail指针指向当前节点的
//fail指针所指向的节点的相同子节点
Q.push(AC[u].vis[i]);//压入队列
}
else//不存在这个子节点
AC[u].vis[i]=AC[AC[u].fail].vis[i];
//当前节点的这个子节点指向当
//前节点fail指针的这个子节点
}
}
}
int AC_Query(string s)//AC自动机匹配
{
int l=s.length();
int now=0,ans=0;
for(int i=0;i<l;++i)
{
now=AC[now].vis[s[i]-'a'];//向下一层
for(int t=now;t&&AC[t].end!=-1;t=AC[t].fail)//循环求解
{
ans+=AC[t].end;
AC[t].end=-1;
}
}
return ans;
}
int main()
{
int n;
string s;
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>s;
Build(s);
}
AC[0].fail=0;//结束标志
Get_fail();//求出失配指针
cin>>s;//文本串
cout<<AC_Query(s)<<endl;
return 0;
}
树上启发式合并
例题,树上数颜色
vector<int>V[MAX];
int co[MAX];
int sz[MAX],hson[MAX]; /// 子树大小和重儿子编号。没有重儿子则编号为0
void dfs1(int x,int f){
sz[x] = 1;
for(auto it : V[x]){
if(it == f)continue;
dfs1(it,x);
sz[x] += sz[it];
if(sz[it] > sz[hson[x]])hson[x] = it;
}
}
int res = 0; /// 目前有多少种不同的颜色
int cnt[MAX],ans[MAX]; /// cnt[i] 表示颜色i的出现次数 ans[i] 表示结点编号i的答案
void go(int x,int f,int dec){
cnt[co[x]] += dec; /// 增 / 删贡献
if(dec == 1 && cnt[co[x]] == 1)res++; /// 多了一种颜色
if(dec ==-1 && cnt[co[x]] == 0)res--; /// 少了一种颜色
for(auto it : V[x]){
if(it == f)continue;
go(it,x,dec);
}
}
void dfs2(int x,int f,bool del){
for(auto it : V[x]){
if(it == f || it == hson[x])continue; /// 遍历轻儿子
dfs2(it,x,true);
}
if(hson[x])dfs2(hson[x],x,false); /// 遍历重儿子
cnt[co[x]]++;if(cnt[co[x]] == 1)res++;
for(auto it : V[x]){
if(it == f || it == hson[x])continue; /// 增加轻儿子贡献
go(it,x,1);
}
ans[x] = res;
if(del)go(x,f,-1); /// 删除轻儿子贡献
}
int main()
{
int n;scanf("%d",&n);
for(int i = 1;i < n;++i){
int ta,tb;scanf("%d%d",&ta,&tb);
V[ta].push_back(tb);
V[tb].push_back(ta);
}
for(int i = 1;i <= n;++i)scanf("%d",&co[i]);
dfs1(1,1);
dfs2(1,1,false);
int Q;scanf("%d",&Q);
while(Q--){
int t;scanf("%d",&t);
printf("%d\n",ans[t]);
}
return 0;
}
主席树
求区间第k小
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
using namespace std;
const int N = 200010;
int n, q, m, cnt = 0;
int a[N], b[N], T[N];
int sum[N<<5], L[N<<5], R[N<<5];
inline int build(int l, int r)
{
int rt = ++ cnt;
sum[rt] = 0;
if (l < r){
L[rt] = build(l, mid);
R[rt] = build(mid+1, r);
}
return rt;
}
inline int update(int pre, int l, int r, int x)
{
int rt = ++ cnt;
L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre]+1;
if (l < r){
if (x <= mid) L[rt] = update(L[pre], l, mid, x);
else R[rt] = update(R[pre], mid+1, r, x);
}
return rt;
}
inline int query(int u, int v, int l, int r, int k)
{
if (l >= r) return l;
int x = sum[L[v]] - sum[L[u]];
if (x >= k) return query(L[u], L[v], l, mid, k);
else return query(R[u], R[v], mid+1, r, k-x);
}
int main()
{
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b+1, b+1+n);
m = unique(b+1, b+1+n)-b-1;
T[0] = build(1, m);
for (int i = 1; i <= n; i ++){
int t = lower_bound(b+1, b+1+m, a[i])-b;
T[i] = update(T[i-1], 1, m, t);
}
while (q --){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
int t = query(T[x-1], T[y], 1, m, z);
printf("%d\n", b[t]);
}
return 0;
}
线性基板子
#include <bits/stdc++.h>
#define ll long long
struct PII{ll x,y;};
using namespace std;
const ll N=1e5+5;
ll p[70],d[N],cnt;
ll insert(ll x)
{
for (int i=62;i>=0;i--)
if (x>>i)//为什么可以不写x&(1<<i)?,因为我们每次x的最高位都是因为x^=p[i]变为0,这样也能判断最高位
if (p[i])
x^=p[i];//如果第i位这个线性基有,同上方的p[6]
else
{
p[i]=x;
return 1;//1代表插入成功
}
return 0; //0代表这个数x能被线性基表示,0需要特判
}
void rebuild()
{
for (int i=0;i<=62;i++)//从低到高
for (int j=i-1;j>=0;j--)//看到比i小的,更新
if (p[i]&(1ll<<j))//如果p[i]的第j位为1,那么就异或成0
//因为此时如果p[j]=0的话1^0=1不变
//如果此时p[j]是存有一个向量的话,就能把这一位的数字异或成0,1^1=0,达到减小的目的
p[i]^=p[j];
for (int i=0;i<=62;i++)
if (p[i])
d[++cnt]=p[i];//存入d数组
}
//求一个序列的若干个元素异或的最大值
ll maxans()
{
ll ans=0;
for(int i=62;i>=0;i--)//记得从线性基的最高位开始
if((ans^p[i])>ans)ans^=p[i];
return ans;
}
ll ask_kth(ll x)
{
if (x>=(1ll<<cnt)) return -1;//越界,不存在
ll res=0;
for (int i=0;i<=62;i++)
if (x&(1ll<<i)) res^=d[i+1];//二进制分解
return res;
}
int main(){
ll n;cin>>n;
for(int i=1;i<=n;i++)
{
ll c;scanf("%lld",&c);
insert(c);
}
rebuild();
for(int i=1;i<=cnt;i++)
cout<<d[i]<<" ";
cout<<endl;
return 0;
}
缩点
把互相能到的点缩成一个点
#include<bits/stdc++.h>
using namespace std;
const int N= 2e5;
int n,m;
vector<int> e[N];
int dfn[N],low[N],tot,a[N];
int instk[N];
int scc[N],siz[N],cnt;
stack<int> q;
void tarjan(int x){
//入x时,盖戳、入栈
dfn[x]=low[x]=++tot;
q.push(x) , instk[x]=1;
for(int y : e[x]){
if(!dfn[y]){//若y尚未访问
tarjan(y);
low[x]=min(low[x],low[y]);//回x时更新low
}
else if(instk[y])//若y已访问且在栈中
low[x]=min(low[x],dfn[y]);//在x时更新low
}
//离x时,收集SCC
if(dfn[x]==low[x]){//若x是SCC的根
int y; ++cnt;
do{
y=q.top();q.pop();//y=stk[top--];
instk[y]=0;
scc[y]=cnt;//SCC编号
++siz[cnt];//SCC大小
}while(y!=x);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1; i<=n; i++)//可能不连通
if(!dfn[i]) tarjan(i);
return 0;
}
/*
5 5
1 2 2 3 3 1 2 5 2 4
*/
马拉车
#include<iostream>
#include<cstring>
using namespace std;
const int N=50000000;
char s[N],ss[N];
int ma[N],id,mx,ans;
int main()
{
cin>>(ss+1);
int n=strlen(ss+1);
for(int i=1;i<=n;i++)
s[i<<1]=ss[i],s[(i<<1)|1]='#';
int len=((int)strlen(ss+1)<<1)|1;
s[0]='&',s[1]='#';
s[len+1]='.';
for(int i=1;i<=len;i++)
{
//cout<<"id mx->"<<id<<" "<<mx<<endl;
if(i<mx) ma[i]=min(ma[(id<<1)-i],mx-i);
while(s[i-ma[i]-1]==s[i+ma[i]+1]) ma[i]++;
if(i+ma[i]>mx)
mx=i+ma[i],id=i;
ans=max(ans,ma[i]);
}
cout<<ans<<endl;
return 0;
}
求回文串
单源最短路径
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
using namespace std;
const int N=5e5+5;
int n,m,p[N],st[N];
vector <pair<int,int> > ed[N];
signed main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,w;
cin>>a>>b>>w;
ed[a].push_back({b,w});
}
vector<int> d(n+5,2e18);
d[1]=0;
priority_queue< PII,vector<PII>,greater<PII> > q;
q.push({0,1});
while(q.size())
{
int u=q.top().second;q.pop();
if(st[u]) continue;
st[u]=1;
for(auto & [v,dd]:ed[u])
{
if(d[v]>d[u]+dd)
{
d[v]=d[u]+dd;
q.push({d[v],v});
}
}
}
for(int i=1;i<=n;i++)
cout<<d[i]<<" \n"[i==n];
return 0;
}
/*
4 6
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
*/
判断负环
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
using namespace std;
const int N=5e5+5;
int n,m;
vector <pair<int,int> > ed[N];
int t;
void spfa()
{
queue<int> q;
vector<int> d(n+5,2e18),cnt(n+5),st(n+5);
d[1]=0;
q.push(1);st[1]=0;
while(q.size())
{
int u=q.front();q.pop();st[u]=0;
for(int i=0;i<ed[u].size();i++)
{
int v=ed[u][i].first,dd=ed[u][i].second;
if(d[v]>d[u]+dd)
{
d[v]=d[u]+dd;cnt[v]=cnt[u]+1;
if(cnt[v]>=n)
{
cout<<"YES\n";return ;
}
if(!st[v]) st[v]=1,q.push(v);
}
}
}
cout<<"NO\n";return ;
}
signed main()
{
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=1;i<=n;i++ ) ed[i].clear();
for(int i=1;i<=m;i++)
{
int a,b,w;scanf("%intd%intd%intd",&a,&b,&w);
if(w>=0)
ed[a].push_back({b,w}),ed[b].push_back({a,w});
else
ed[a].push_back({b,w});
}
spfa();
}
return 0;
}
最近公共祖先
#include <bits/stdc++.h>
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define SIZE 400010
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int n, m, k, cnt;
int pa[SIZE][21], dep[SIZE];
vector<int> vec[SIZE]; //邻接表
void dfs(int rt, int fin) { //预处理深度和祖先
pa[rt][0] = fin;
dep[rt] = dep[pa[rt][0]] + 1; //深度
for (int i = 1; i < 21; ++i)
pa[rt][i] = pa[pa[rt][i - 1]][i - 1]; // rt 的 2^i 祖先等价于 rt 的 2^(i-1) 祖先 的 2^(i-1) 祖先
int sz = vec[rt].size();
for (int i = 0; i < sz; ++i) {
if (vec[rt][i] == fin) continue;
dfs(vec[rt][i], rt);
}
}
int LCA(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
int tmp = dep[y] - dep[x];
for (int j = 0; tmp; ++j, tmp >>= 1)
if (tmp & 1) y = pa[y][j]; //先跳到同一高度
if (y == x) return x;
for (int j = 20; j >= 0 && y != x; --j) { //从底往上跳
if (pa[x][j] != pa[y][j]) { //如果当前祖先不相等 我们就需要更新
x = pa[x][j];
y = pa[y][j];
}
}
return pa[x][0];
}
int main() {
io(); cin >> n >> m;
rep(i, 1, (n - 1)) {
int x, y; cin >> x >> y;
vec[x].emplace_back(y);
vec[y].emplace_back(x);
}
dfs(1, 0);
while(1)
{
int a,b;
cin>>a>>b;
cout<<LCA(a,b)<<endl;
}
}
int n,m;
vector<int> ed[N];
int father[N];
int dp[N][20];
int dep[N];
void dfs(int u,int fa)
{
father[u] = fa;
dep[u] = dep[fa]+1;
for(auto v:ed[u] )
{
if(v==fa)
continue;
dfs(v,u);
}
}
int lca(int u,int v)
{
//da xiao
if( dep[u]<dep[v] )
swap(u,v);
for(int i=dep[u]-dep[v] ,j=0 ; i ; i>>=1ll , j++ )
{
if( i&1ll )
{
u = dp[u][j];
}
}
if(u==v)
return u;
for(int i=19;i>=0;i--)
{
if( dp[u][i] != dp[v][i] )
{
u = dp[u][i];
v = dp[v][i];
}
}
return dp[u][0];
}
void init()
{
dfs(1,0);
for(int i=1;i<=n;i++)
dp[i][0] = father[i];
for(int i=1;i<20;i++)
for(int j=1;j<=n;j++ )
{
dp[j][i] = dp[dp[j][i-1]][i-1] ;
}
}
计算几何
点(x,y)逆时针寻转&度 后得到(x' , y')
数论
欧拉函数
[1-x]中与x 互质的个数
int oula(int x)
{
int res=x;
for(int i=2;i*i<=x;i++)
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0)
x/=i;
}
if(x>1)
res=res/x*(x-1);
return res;
}
求x的因素的个数
int factor_num(int x)
{
int res=1;
for(int i=2;i*i<=x;i++)
if(x%i==0)
{
ll cnum=0;
while(x%i==0)
cnum++,x/=i;
res*=cnum+1;res%=mod;
}
if(x>1)
res*=2,res%=mod;
return res;
}
扩展欧几里得
a*x + b*y = gcd( a , b )
int exgcd(int a, int b, int &x, int &y)
{
if(!b)return x = 1, y = 0, a;
int d = exgcd(b, a % b, y , x);
y -= a / b * x;
return d;
}
返回的是a和b的最大公约数
x 的周期是 abs( b / gcd( a,b ) )
y 的周期是 abs( a / gcd( a,b ) )
组合学板子
int fpow(int x,int y)
{
int res = 1;
while(y)
{
if(y&1ll)
res *= x , y--, res %= mod;
else
x *= x , x %= mod, y>>=1ll;
}
return res;
}
const int N=2000005;
int fac[N],inv[N];
void init()
{
fac[0]=1,inv[0]=1;
for(int i=1;i<N;i++)
fac[i]=fac[i-1]*i%mod;
inv[N-1]=fpow(fac[N-1],mod-2);
for(int i=N-2;i>=1;i--)
inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int aa,int bb)
{
return fac[bb] %mod* inv[bb-aa]%mod * inv[aa]%mod;
}
整数分块
int block(int st, int ed, int num) {
//sum(num/i i in [st,ed])
int L = 0;
int _ans = 0;
ed = min(ed, num);
for (int i = st; i <= ed; i = L + 1) {
L = min(ed,num / (num / i)); //该区间的最后一个数
_ans += (L - i + 1)*(num / i);//区间[i,L]的num/i 都是一个值
}
return _ans;
}
该函数的作用是计算指定范围内num/i
的和,其中i的取值范围是从st
到ed
。
小知识点
优先队列
priority_queue <ll,vector<ll>,greater<ll> >q; 栈顶到栈底是从小到大 priority_queue <ll >q;栈顶到栈底是从大到小;
无序哈希
unordered_map 查找效率:非常高,查找的时间复杂度可达到O(1),最坏的情况下卡到O(n) unordered_map 是否有序:无序
排序
sort(a+1,a+n+1,greater<ll>());从大到小 sort(a+1,a+n+1); 从小到大
multiset
自动排序 复杂度(log n)
可以存多个同一种元素
multiset<int> v; v.lower_bound(aim) 在v中找>=aim 的第一个元素 返回指针 v.upper_bound(elem); 返回第一个>aim元素的迭代器。 end(v) 这个位置没有元素,是v中最后一位
v.erase(5) ; 删除所有的5
v.erase( v.find(5) ); 删除一个5
容斥定理
并:(集合的大小)奇正偶负 交:全集减补集的并
小数点的控制
//c++ double a = 0.123 cout<<setprecision(5)<<a<<endl;//输出 0.123
//c printf("%.5lf",a);
输入一行
char s[8];
gets(s); //c
string s;
getline(cin,s);//c++
PI
pi = acos(-1);
数学小知识
map
map<int,int> mp;
mp[5] = 7;
mp.erase(5) //把5的键值给擦除掉
mp.count(5) //判断是否有5的键值
运算符重载
struct node
{
int a,d;
bool operator < (const node &b)const
{
return this->d > b.d;
}
};
用 sort 排序后后从大到小排序
交互题格式
fflush(stdin);清空输入缓冲区
fflush(stdout);清空输出缓冲区
快读
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
其它
1到1e9中有大约有5e7多个质素
#include <bits/stdc++.h>
#define int long long
using namespace std;
int f[340000],g[340000],n;
void init(){
int i,j,m;
for(m=1;m*m<=n;++m)f[m]=n/m-1;
for(i=1;i<=m;++i)g[i]=i-1;
for(i=2;i<=m;++i){
if(g[i]==g[i-1])continue;
for(j=1;j<=min(m-1,n/i/i);++j){
if(i*j<m)f[j]-=f[i*j]-g[i-1];
else f[j]-=g[n/i/j]-g[i-1];
}
for(j=m;j>=i*i;--j)g[j]-=g[j/i]-g[i-1];
}
}
signed main(){
while(scanf("%I64d",&n)!=EOF){
init();
cout<<f[1]<<endl;
}
return 0;
}