ACM竞赛板子
文章目录
自己的板子积累,不断更新中
基本算法
贪心
区间覆盖问题
给出 m m m 个区间,选出最少的区间个数覆盖1~n.
struct node {
int l,r;
bool operator<(const node &t) const {
return l<t.l;
}
};
int main() {
vector<node> ve;
int n,m;
cin>>n>>m;
rep(i,1,m) {
int l,r;
cin>>l>>r;
ve.pushk({l,r});
}
sort(ve.begin(),ve.end());
int s=1;
int ans=0;
rep(idx,0,m-1) {
int tmp=0;
int j=idx;
while(ve[j].l<=s && j<=m-1) tmp=max(tmp,ve[j++].r);
//这里根据题目情况而定,有可能是s=tmp
s=tmp+1;
ans++;
idx=j-1;
if(s>m) break;
}
cout<<ans<<endl;
return 0;
}
快速幂加龟速乘
ll ksc(ll x,ll k)
{
ll res=0;
while(k)
{
if(k&1) res=(res+x)%mod;
k>>=1;
x=(x+x)%mod;
}
return res;
}
ll qm(ll x,ll k)
{
ll res=1;
while(k)
{
if(k&1) res=ksc(res,x);
k>>=1;
x=ksc(x,x);
}
return (ll)res;
}
ST表
int f[N][20];//f[i][j]表示i开始的2^j个数的最大值
int n,q;
void init(){
rep(i,1,n) f[i][0] = a[i];
int t = __lg(n)+1;
for(int j=1; j<t; ++j){
for(int i=1; i<=n-(1<<j)+1; ++i){
f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]);
}
}
}
int ask(int l,int r){
int k = __lg(r-l+1);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
数学
区间互质
namespace qjhz {
typedef long long ll;
ll T, N, num;
ll A, B;
ll a[100];
void prime(ll n) {
num = 0;
for (ll i = 2; i*i <= n; i++) {
if ((n%i) == 0) {
num++;
a[num] = i;
while ((n%i) == 0) {
n /= i;
}
}
}
if (n > 1) {
num++;
a[num] = n;
}
return;
}
ll solve(ll r, ll n) {
prime(n);
ll res = 0;
for (ll i = 1; i < (1 << num); i++) {
ll kk = 0;
ll div = 1;
for (ll j = 1; j <= num; j++) {
if (i&(1 << (j - 1))) {
kk++;
div *= a[j];
}
}
if (kk % 2)
res += r / div;
else
res -= r / div;
}
return r - res;
}
ll que(ll L, ll R, ll k) {
return solve(R, k) - solve(L - 1, k);
}
}
三角形成型条件
若 a , b , c a,b,c a,b,c 满足, a b s ( b − c ) < a < b + c abs(b-c)<a<b+c abs(b−c)<a<b+c , 则 a , b , c a,b,c a,b,c 可以构成三角形。
合法括号序列
长度为 2 n 2n 2n 且包含 k k k 种不同类型的括号的合法括号序列数量为:
f n = 1 n + 1 ∗ C 2 n n ∗ k n f_n = \frac{1}{n+1}*C_{2n}^n*k^n fn=n+11∗C2nn∗kn
指数取模
出处:
https://blog.csdn.net/doyouseeman/article/details/51863382
欧拉函数
参考:https://www.acwing.com/solution/content/3952/
void solve(int n){
phi[1]=1;
for (int i = 2; i <=n; ++i ){
if(!st[i]) p[tot++]=i,phi[i]=i-1;
for(int j=0; p[j]<=n/i; ++j){
st[p[j]*i] = true;
if(i%p[j]==0) {
phi[i*p[j]] = phi[i]*p[j];
break;
}
phi[i*p[j]] = phi[i]*(p[j]-1);
}
}
}
exgcd求逆元
void exgcd(int a, int b, int &x, int &y){
if(!b) x = 1, y = 0;
else{
exgcd(b, a % b, y, x);
y -= a / b * x;
}
}
int main(){
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d", (x % b + b) % b);
return 0;
}
递推法求逆元
inv[i]=(mod-mod/i)*inv[mod%i]%mod (其中mod为模数,要求为奇质数
递推法预处理阶乘逆元
void init(){
f[0]=inf[0]=inf[1]=f[1]=1;
rep(i,2,N-2){
f[i]=1ll*f[i-1]*i%mod;
inf[i]=1ll*(mod-mod/i)*inf[mod%i]%mod;
}
rep(i,2,N-2){
inf[i]=inf[i-1]*inf[i]%mod;
}
}
卢卡斯定理
int qmi(int x,int k){
int res=1;
while(k){
if(k&1) res=1ll*res*x%mod;
k>>=1;
x=1ll*x*x%mod;
}
return res;
}
int C(int n,int m){
if(m>n) return 0;
return 1ll*f[n]*qmi(f[m],mod-2)%mod*qmi(f[n-m],mod-2)%mod;
}
int lucas(ll n,ll m){
if(!m) return 1;
return 1ll*C(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
}
void init(){
f[0]=1;
rep(i,1,mod){
f[i]=1ll*f[i-1]*i%mod;
}
}
判断质数
质数分布的规律:大于等于5的质数一定和6的倍数相邻
参考大佬:https://blog.csdn.net/songyunli1111/article/details/78690447
int isPrime(int n)
{ //返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
float n_sqrt;
if(n==2 || n==3) return 1;
if(n%6!=1 && n%6!=5) return 0;
n_sqrt=floor(sqrt((float)n));
for(int i=5;i<=n_sqrt;i+=6)
{
if(n%(i)==0 | n%(i+2)==0) return 0;
}
return 1;
}
欧拉筛(线性筛)
时间复杂度O(n)
void prime(int n)
{
for(int i=2; i<=n; i++)
{
if(!st[i]) p[ans++]=i;
for(int j=0; p[j]<=n/i; j++)
{
st[p[j]*i]=1;
if(i%p[j]==0) break;
}
}
}
阶乘和阶乘逆元,组合数(递推)
int qmi(int x,int k)
{
int res=1;
while(k)
{
if(k&1) res=1ll*res*x%mod;
k>>=1;
x=1ll*x*x%mod;
}
return res;
}
void init()
{
f[0]=inf[0]=1;
for(int i=1; i<N; i++)
{
f[i]=1ll*f[i-1]*i%mod;
inf[i]=1ll*inf[i-1]*qmi(i,mod-2)%mod;
}
}
计算几何
点到线段的最短距离
参考:https://blog.csdn.net/Mr_HCW/article/details/82816490
typedef struct node
{
double x, y;
}Point;
double getDis2(Point a, Point b) //得到两点距离的平方
{
return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}
double disMin(Point A, Point B,Point P)
{
double r = ((P.x - A.x)*(B.x - A.x) + (P.y - A.y)*(B.y - A.y)) / getDis2(A,B);
if (r <= 0) return sqrt(getDis2(A,P)); //第一种情况, 返回AP的长
else if (r >= 1) return sqrt(getDis2(B,P)); //第二种情况, 返回BP的长度
else //第三种情况, 返回PC的长度
{
double AC = r*sqrt(getDis2(A,B)); //先求AC的长度,(AC=r*|AB|)
return sqrt(getDis2(A,P)-AC*AC); //再勾股定理返回PC的长度
}
二维几何
kuangbin计算几何板子
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
//`Compares a double to zero`
int sgn(double x){
if(fabs(x) < eps)return 0;
if(x < 0)return -1;
else return 1;
}
//square of a double
inline double sqr(double x){return x*x; }
struct Point{
double x,y;
Point(){}
Point(double _x,double _y){
x = _x;
y = _y;
}
void input(){
scanf("%lf%lf",&x,&y);
}
void output(){
printf("%.2f %.2f\n",x,y);
}
bool operator == (Point b)const{
return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
}
bool operator < (Point b)const{
return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
//叉积
double operator ^(const Point &b)const{
return x*b.y - y*b.x;
}
//点积
double operator *(const Point &b)const{
return x*b.x + y*b.y;
}
//返回长度
double len(){
return hypot(x,y);//库函数
}
//返回长度的平方
double len2(){
return x*x + y*y;
}
//返回两点的距离
double distance(Point p){
return hypot(x-p.x,y-p.y);
}
Point operator +(const Point &b)const{
return Point(x+b.x,y+b.y);
}
Point operator *(const double &k)const{
return Point(x*k,y*k);
}
Point operator /(const double &k)const{
return Point(x/k,y/k);
}
//`计算pa 和 pb 的夹角`
//`就是求这个点看a,b 所成的夹角`
//`测试 LightOJ1203`
double rad(Point a,Point b){
Point p = *this;
return fabs(atan2( fabs((a-p)^(b-p)),(a-p)*(b-p) ));
}
//`化为长度为r的向量`
Point trunc(double r){
double l = len();
if(!sgn(l))return *this;
r /= l;
return Point(x*r,y*r);
}
//`逆时针旋转90度`
Point rotleft(){
return Point(-y,x);
}
//`顺时针旋转90度`
Point rotright(){
return Point(y,-x);
}
//`绕着p点逆时针旋转angle`
Point rotate(Point p,double angle){
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x*c - v.y*s,p.y + v.x*s + v.y*c);
}
};
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
bool operator ==(Line v){
return (s == v.s)&&(e == v.e);
}
//`根据一个点和倾斜角angle确定直线,0<=angle<pi`
Line(Point p,double angle){
s = p;
if(sgn(angle-pi/2) == 0){
e = (s + Point(0,1));
}
else{
e = (s + Point(1,tan(angle)));
}
}
//ax+by+c=0
Line(double a,double b,double c){
if(sgn(a) == 0){
s = Point(0,-c/b);
e = Point(1,-c/b);
}
else if(sgn(b) == 0){
s = Point(-c/a,0);
e = Point(-c/a,1);
}
else{
s = Point(0,-c/b);
e = Point(1,(-c-a)/b);
}
}
void input(){
s.input();
e.input();
}
void adjust(){
if(e < s)swap(s,e);
}
//求线段长度
double length(){
return s.distance(e);
}
//`返回直线倾斜角 0<=angle<pi`
double angle(){
double k = atan2(e.y-s.y,e.x-s.x);
if(sgn(k) < 0)k += pi;
if(sgn(k-pi) == 0)k -= pi;
return k;
}
//`点和直线关系`
//`1 在左侧`
//`2 在右侧`
//`3 在直线上`
int relation(Point p){
int c = sgn((p-s)^(e-s));
if(c < 0)return 1;
else if(c > 0)return 2;
else return 3;
}
// 点在线段上的判断
bool pointonseg(Point p){
return sgn((p-s)^(e-s)) == 0 && sgn((p-s)*(p-e)) <= 0;
}
//`两向量平行(对应直线平行或重合)`
bool parallel(Line v){
return sgn((e-s)^(v.e-v.s)) == 0;
}
//`两线段相交判断`
//`2 规范相交`
//`1 非规范相交`
//`0 不相交`
int segcrossseg(Line v){
int d1 = sgn((e-s)^(v.s-s));
int d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s));
int d4 = sgn((v.e-v.s)^(e-v.s));
if( (d1^d2)==-2 && (d3^d4)==-2 )return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
(d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
(d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
(d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
//`直线和线段相交判断`
//`-*this line -v seg`
//`2 规范相交`
//`1 非规范相交`
//`0 不相交`
int linecrossseg(Line v){
int d1 = sgn((e-s)^(v.s-s));
int d2 = sgn((e-s)^(v.e-s));
if((d1^d2)==-2) return 2;
return (d1==0||d2==0);
}
//`两直线关系`
//`0 平行`
//`1 重合`
//`2 相交`
int linecrossline(Line v){
if((*this).parallel(v))
return v.relation(s)==3;
return 2;
}
//`求两直线的交点`
//`要保证两直线不平行或重合`
Point crosspoint(Line v){
double a1 = (v.e-v.s)^(s-v.s);
double a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
//点到直线的距离
double dispointtoline(Point p){
return fabs((p-s)^(e-s))/length();
}
//点到线段的距离
double dispointtoseg(Point p){
if(sgn((p-s)*(e-s))<0 || sgn((p-e)*(s-e))<0)
return min(p.distance(s),p.distance(e));
return dispointtoline(p);
}
//`返回线段到线段的距离`
//`前提是两线段不相交,相交距离就是0了`
double dissegtoseg(Line v){
return min(min(dispointtoseg(v.s),dispointtoseg(v.e)),min(v.dispointtoseg(s),v.dispointtoseg(e)));
}
//`返回点p在直线上的投影`
Point lineprog(Point p){
return s + ( ((e-s)*((e-s)*(p-s)))/((e-s).len2()) );
}
//`返回点p关于直线的对称点`
Point symmetrypoint(Point p){
Point q = lineprog(p);
return Point(2*q.x-p.x,2*q.y-p.y);
}
};
平面最近点对
n l o g ( n ) nlog(n) nlog(n)
const int MAXN = 100010;
const double eps = 1e-8;
const double INF = 1e20;
struct Point{
double x,y;
void input(){
scanf("%lf%lf",&x,&y);
}
};
double dist(Point a,Point b){
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
Point p[MAXN];
Point tmpt[MAXN];
bool cmpx(Point a,Point b){
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
bool cmpy(Point a,Point b){
return a.y < b.y || (a.y == b.y && a.x < b.x);
}
double Closest_Pair(int left,int right){
double d = INF;
if(left == right)return d;
if(left+1 == right)return dist(p[left],p[right]);
int mid = (left+right)/2;
double d1 = Closest_Pair(left,mid);
double d2 = Closest_Pair(mid+1,right);
d = min(d1,d2);
int cnt = 0;
for(int i = left;i <= right;i++){
if(fabs(p[mid].x - p[i].x) <= d)
tmpt[cnt++] = p[i];
}
sort(tmpt,tmpt+cnt,cmpy);
for(int i = 0;i < cnt;i++){
for(int j = i+1;j < cnt && tmpt[j].y - tmpt[i].y < d;j++)
d = min(d,dist(tmpt[i],tmpt[j]));
}
return d;
}
int main(){
int n;
while(scanf("%d",&n) == 1 && n){
for(int i = 0;i < n;i++)p[i].input();
sort(p,p+n,cmpx);
printf("%.2lf\n",Closest_Pair(0,n-1));
}
return 0;
}
高斯消元求行列式的值
参考:https://blog.csdn.net/xyz32768/article/details/81413569
数组a存储的是矩阵
ll det() {
int i, j, k;
ll res = 1;
for(i=1; i<=n; ++i){
ll qaq = qpow(a[i][i], mod- 2);
for( j=i + 1; j<=n; ++j) {
ll tmp = 1ll * a[j][i] * qaq % mod;
for(k=1; k<=n; ++k) a[j][k] = (a[j][k] - 1ll * a[i][k] *
tmp % mod + mod) % mod;
}
}
for(i=1; i<=n; ++i) res = 1ll * res * a[i][i] % mod;
return (res+mod)%mod;
}
数据结构和图论
迪杰斯特拉
struct ShortPath{
int head[N],to[M],len[M],nxt[M],tot;
int dis[N],vis[N];
void init() {
tot=0;
memset(head,0,sizeof(head));
}
void add(int u,int v,int w) {
to[++tot]=v;len[tot]=w;nxt[tot]=head[u];head[u]=tot;
}
void dijkstra() {
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
priority_queue<PII> pq;
dis[s]=0;
pq.push(make_pair(0,s));
while(!pq.empty()) {
int u=pq.top().second;
pq.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=nxt[i])
if(dis[to[i]]>dis[u]+len[i]) {
dis[to[i]]=dis[u]+len[i];
pq.push(make_pair(-dis[to[i]],to[i]));
}
}
}
}sp,sn;
滑动窗口
int n,k;
cin>>n>>k;
rep(i,1,n) cin>>a[i];
rep(i,1,n) {
//约束区间长度
while(hh<=tt && q[hh]<=i-k) ++hh;
//约束单调性
while(hh<=tt && a[q[tt]]>=a[i]) --tt;
q[++tt]=i;
if(i>=k) printf("%d ",a[q[hh]]);
}
hh=1,tt=0;
puts("");
rep(i,1,n) {
//约束区间长度
while(hh<=tt && q[hh]<=i-k) ++hh;
//约束单调性
while(hh<=tt && a[q[tt]]<=a[i]) --tt;
q[++tt]=i;
if(i>=k) printf("%d ",a[q[hh]]);
}
树上倍增求LCA
int pa[N][20];//pa[i][j]:i的2^i级祖先
int dep[N],lg[N];
int h[N],e[N*2],ne[N*2],idx;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a], h[a]=idx++;
}
void dfs(int u,int fa){
dep[u] = dep[fa] + 1;
pa[u][0] = fa;
//核心转移数组,u的2^i级祖先等于u的2^(i-1)级祖先的2^(i-1)级祖先
//2^i = 2^(i-1) + 2^(i-1)
//这里只能从小到大更新,因为pa[u][i]依赖pa[u][i-1]的值
for(int i=1; i<=lg[dep[u]]-1; ++i){
pa[u][i] = pa[pa[u][i-1]][i-1];
}
for(int i=h[u]; ~i; i=ne[i]){
int it = e[i];
if(it==fa) continue;
dfs(it,u);
}
}
int lca(int x,int y){
//设定dep[x]>dep[y],方便处理
if(dep[x]<dep[y]) swap(x,y);
//先跳到统一深度
//这里lg[]-1,是因为lg[i]的含义是:log2(i)+1 ,这个可以自己修改
while(dep[x]>dep[y]) x = pa[x][lg[dep[x] - dep[y]]-1];
if(x==y) return x;//x是y的祖先的情况
for(int i=lg[dep[x]]-1; i>=0; i--){
//我们要跳到他们Lca的下一层,不能直接跳到Lca因为可以能会误判
//因为他们的lca的祖先也满足 pa[x][i]==pa[y][i]
if(pa[x][i]!=pa[y][i]) x=pa[x][i],y=pa[y][i];
}
return pa[x][0];
}
int main() {
mem(h, -1);
rep(i,1,N-9) lg[i] = lg[i-1] + ((1<<lg[i-1])==i);
int n,m,s;
cin>>n>>m>>s;
rep(i,1,n-1){
int u,v;
scanf("%d %d",&u, &v);
add(u,v),add(v,u);
}
//dfs预处理出pa数组
dfs(s,0);
while(m--){
int u,v;
scanf("%d %d",&u,&v);
printf("%d\n",lca(u,v));
}
return 0;
}
主席树
区间第k小
struct node{
int ls,rs;//左右子树编号
int cnt;
}tr[N*4+17*N];
int rt[N],n,m,a[N],idx;//存储版本i的根节点的编号
vector<int> ve;
int get(int x){
return lower_bound(ve.begin(), ve.end(),x)-ve.begin()+1;
}
//建树 ,此时的cnt为0
int build(int l,int r){
int q = ++idx;
if(l>=r) return q;
int mid = l+r>>1;
tr[q].ls = build(l,mid),tr[q].rs=build(mid+1,r);
return q;
}
int insert(int p,int l,int r,int x){
int q = ++idx;
tr[q]=tr[p];
if(l>=r) {
tr[q].cnt++;
return q;
}
int mid = l+r>>1;
if(x<=mid) tr[q].ls=insert(tr[p].ls,l,mid,x);
else tr[q].rs = insert(tr[p].rs,mid+1,r,x);
tr[q].cnt = tr[tr[q].ls].cnt + tr[tr[q].rs].cnt;
return q;
}
int ask(int q,int p,int l,int r,int k){
if(l>=r) return l;
int cnt = tr[tr[q].ls].cnt - tr[tr[p].ls].cnt;
int mid = l+r>>1;
if(k<=cnt) return ask(tr[q].ls,tr[p].ls,l,mid,k);
else return ask(tr[q].rs,tr[p].rs,mid+1,r,k-cnt);
}
int main() {
cin>>n>>m;
rep(i,1,n) scanf("%d",a+i),ve.emk(a[i]);
sort(ve.begin(),ve.end());
ve.erase(unique(ve.begin(), ve.end()),ve.end());
rt[0]=build(1,ve.size());
rep(i,1,n) rt[i]=insert(rt[i-1],1,ve.size(),get(a[i]));
while(m--){
int l,r,k;
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",ve[ask(rt[r],rt[l-1],1,ve.size(),k)-1]);
}
return 0;
}
线段树
#define ll long long
#define lp p<<1
#define rp p<<1|1
struct SegT {
int l,r;
ll sum;
ll add;
} tr[N*4];
void build(int p,int l,int r) {
tr[p].l=l,tr[p].r=r;
if(l>=r) {
tr[p].sum=a[l],tr[p].add=0;
return ;
}
int mid = (l+r)>>1;
build(lp,l,mid);
build(rp,mid+1,r);
tr[p].sum = tr[lp].sum + tr[rp].sum;
}
void spread(int p) {
if(tr[p].add) {
tr[lp].sum+=1ll*(tr[lp].r-tr[lp].l+1)*tr[p].add;
tr[rp].sum+=1ll*(tr[rp].r - tr[rp].l +1 )*tr[p].add;
tr[lp].add+=tr[p].add;
tr[rp].add+=tr[p].add;
tr[p].add=0;
}
}
void change(int p,int l,int r,ll k) {
if(tr[p].l>=l && tr[p].r <= r) {
tr[p].sum+=1ll*(tr[p].r-tr[p].l+1)*k;
tr[p].add+=k;
return ;
}
spread(p);
int mid = (tr[p].l + tr[p].r)>>1;
if(l<=mid) change(lp,l,r,k);
if(r>mid) change(rp,l,r,k);
tr[p].sum = tr[lp].sum + tr[rp].sum;
}
ll ask(int p,int l,int r) {
if(tr[p].l>=l && tr[p].r <= r) {
return tr[p].sum;
}
spread(p);
int mid = (tr[p].l + tr[p].r)>>1;
ll res=0;
if(l<=mid) res+=ask(lp,l,r);
if(r>mid) res+=ask(rp,l,r);
tr[p].sum = tr[lp].sum + tr[rp].sum;
return res;
}
带权并查集
参考:https://blog.csdn.net/yjr3426619/article/details/82315133
hdu3038:https://acm.hdu.edu.cn/showproblem.php?pid=3038
int find(int x){
if(x!=p[x]) {
int t=p[x];
p[x]=find(p[x]);
val[x]+=val[t];
}
return p[x];
}
void merge(int a,int b,int c){
int fa=find(a),fb=find(b);
if(fa!=fb){
p[fa]=fb;
val[fa]=val[b]-val[a]+1ll*c;
}
}
int main(){
int n,m;
while(~scanf("%d %d",&n,&m)){
for(int i=0; i<=n; ++i) p[i]=i,val[i]=0;
int ans=0;
for(int i=0; i<m; ++i){
int a,b;
ll c;
scanf("%d %d %lld",&a,&b,&c);
a--;
int fa=find(a),fb=find(b);
if(fa!=fb){
merge(a,b,c);
}
else{
if(val[a]-val[b]!=c) ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
dfs序
void Dfs(int u, int fa, int dep)
{
seq[++cnt] = u;
st[u] = cnt;
int len = edge[u].size();
for(int i = 0; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u, dep+1);
}
}
seq[++cnt] = u;
ed[u] = cnt;
}
小根堆的建立
边输入边建立,相当于在在堆中插入一个新的元素,每次直接放到末尾,然后进行堆化。
找到第一个非叶节点进行堆化,
n
/
2
n/2
n/2
//堆化
void build(int n){
int t=n;
while(t/2>=1 && a[t/2]>a[t]){
swap(a[t/2],a[t]);
t/=2;
}
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1; i<=n; i++){
scanf("%d",a+i);
build(i);
}
}
字典树
const int N =1e5+9;
const int M = 30*N;
int son[M][2],idx;
int a[N];
void insert(int x){
int p=0;
rep(i,0,30){
int t=x>>i&1;
if(!son[p][t]) son[p][t]=++idx;
p=son[p][t];
}
}
int ask(int x){
int p=0;
int res=0;
rep(i,0,30){
int t=x>>i&1;
if(son[p][!t]){
p=son[p][!t];
res+=1<<i;
}
else p=son[p][t];
}
return res;
}
int main(){
int n;
cin>>n;
rep(i,0,n-1) scanf("%d",a+i),insert(a[i]);
int res=0;
rep(i,0,n-1){
res=max(res,ask(a[i]));
}
cout<<res<<'\n';
return 0;
}
动态规划
环形石子合并
把环形链扩展成2倍数组
int n;
cin>>n;
rep(i,1,n) cin>>a[i],sum[i]=sum[i-1]+a[i];
rep(i,n+1,2*n-1) a[i]=a[i-n],sum[i]=sum[i-1]+a[i];
//puts("");
rep(len,2,n){
rep(j,1,2*n-1){
int l=j,r=j+len-1;
if(r>2*n-1) break;
f[l][r]=INF;
rep(k,l,r) f[l][r]=min(f[l][r], f[l][k]+f[k+1][r]+sum[r]-sum[l-1]);
}
}
int ans=INF;
rep(i,1,n) {
//cout<<i<<" "<<i+n-1<<' '<<f[i][i+n-1]<<'\n';
ans=min(ans,f[i][i+n-1]);
}
cout<<ans<<'\n';
最长公共子序列滚动数组写法
int p=0;
rep(i,1,n){
p=1-p;
rep(j,1,n){
if(a[i]==b[j]) f[p][j]=f[1-p][j-1]+1;
else f[p][j]=max(f[1-p][j],f[p][j-1]);
}
}
cout<<f[p][n]<<'\n';
数位dp
此模板来源于洛谷大佬:
https://www.luogu.com.cn/blog/virus2017/shuweidp
ll dfs(int pos,int pre,int st,……,int lead,int limit)//记搜
{
if(pos>len) return st;//剪枝
if((dp[pos][pre][st]……[……]!=-1&&(!limit)&&(!lead))) return dp[pos][pre][st]……[……];//记录当前值
ll ret=0;//暂时记录当前方案数
int res=limit?a[len-pos+1]:9;//res当前位能取到的最大值
for(int i=0;i<=res;i++)
{
//有前导0并且当前位也是前导0
if((!i)&&lead) ret+=dfs(……,……,……,i==res&&limit);
//有前导0但当前位不是前导0,当前位就是最高位
else if(i&&lead) ret+=dfs(……,……,……,i==res&&limit);
else if(根据题意而定的判断) ret+=dfs(……,……,……,i==res&&limit);
}
if(!limit&&!lead) dp[pos][pre][st]……[……]=ret;//当前状态方案数记录
return ret;
}
ll part(ll x)//把数按位拆分
{
len=0;
while(x) a[++len]=x%10,x/=10;
memset(dp,-1,sizeof dp);//初始化-1(因为有可能某些情况下的方案数是0)
return dfs(……,……,……,……);//进入记搜
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&l,&r);
if(l) printf("%lld",part(r)-part(l-1));//[l,r](l!=0)
else printf("%lld",part(r)-part(l));//从0开始要特判
}
return 0;
}
完全背包
每种物品能用无限多次
int a[N],b[N];
int f[N];
int main() {
int n,m;
cin>>n>>m;
rep(i,1,n){
cin>>a[i]>>b[i];
}
rep(i,1,n){
rep(j,a[i],m){
f[j]=max(f[j],f[j-a[i]]+b[i]);
}
}
cout<<f[m]<<'\n';
return 0;
}
多重背包二进制优化
每种物品最多用 x i x_i xi 次
int v[N],w[N],tot;
int f[N];
int main() {
int n,m;
cin>>n>>m;
rep(i,1,n){
int x,y,z;
cin>>x>>y>>z;
int t=1;
while(z>=t){
v[++tot]=t*x,w[tot]=t*y;
z-=t;
t<<=1;
}
if(z) v[++tot]=z*x,w[tot]=z*y;
}
n=tot;
rep(i,1,n){
per(j,m,v[i]){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<'\n';
多重背包单调队列优化
int f[N],g[N],q[N];
int main() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; ++i){
memcpy(g,f,sizeof f);
int v,w,s;
cin>>v>>w>>s;
for(int j=0; j<v; ++j){
//我们按照余数分类,对于每个j,我们用单调队列维护
//f[j]=max(f[j-v]+w,f[j-2v]+2w,...f[j-sv]+sw);
int hh=1,tt=0;
for(int k=j; k<=m; k+=v){
if(hh<=tt && q[hh]<k-s*v) ++hh;
if(hh<=tt) f[k]=max(g[k],g[q[hh]]+(k-q[hh])/v*w);
while(hh<=tt && g[q[tt]]+(k-q[tt])/v*w<=g[k]) --tt;
q[++tt]=k;
}
}
}
cout<<f[m]<<'\n';
分祖背包
int f[N];
int v[N][N],w[N][N];
int main() {
int n,m;
cin>>n>>m;
rep(i,1,n){
int x,y,z;
cin>>x>>y>>z;
v[x][++v[x][0]]=y;
w[x][++w[x][0]]=z;
}
rep(i,1,n){
per(j,m,1){
rep(k,1,v[i][0]){
if(j>=v[i][k])
f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[m]<<'\n';
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话