【一些要要补的东西】
1.crt
2.虚树,概率(卑微……)
3.支配树,圆方树
0x00基本算法
0x01 位运算
1.补码
2.移位运算
快速幂
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } int a,b,p; int main() { rd(a),rd(b),rd(p); int ans=1%p; for(;b;b>>=1) { if(b&1)ans=(long long)ans*a%p; a=(long long)a*a%p; } printf("%d",ans); re 0; }
龟速乘
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } typedef long long ll; ll a,b,p; int main() { /*龟速乘 */ rd(a),rd(b),rd(p); ll ans=0; for(;b;b>>=1) { if(b&1)ans=(ans+a)%p; a=(a<<1)%p; } printf("%lld",ans); re 0; }
3.状压
4.成对变换
5.lowbit
0x02递推与递归
1.概论
1. 奇怪的汉诺塔
在三的基础上搞四
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } int n,m,f[16],d[16]; int main() { memset(f,0x3f,sizeof f); f[1]=d[1]=1; inc(i,2,12) { inc(j,1,i-1) f[i]=min(f[i],2*f[j]+d[i-j]); d[i]=d[i-1]<<1|1; } inc(i,1,12) printf("%d\n",f[i]); re 0; }
2.费解的开关
强势枚举第一层
暴力计算可行度
2.分治
3.分形
分型之城
恶心的分治模拟
//一道模拟还要开longlong #include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } typedef long long ll; struct nide{ ll x,y; }pa,pb; ll N,a,b; inline nide dfs(ll x,ll n) { if(!n)re (nide){0,0}; ll len=1ll<<(n-1);//2的n-1次方 ll cnt=1ll<<((n-1)<<1);//4^(n-1),城市数 ll pos=x/cnt; nide ansnow=dfs(x%cnt,n-1); if(!pos) re (nide){ansnow.y,ansnow.x}; if(pos==1) re (nide){ansnow.x,len+ansnow.y}; if(pos==2) re (nide){len+ansnow.x,ansnow.y+len}; if(pos==3) re (nide){2*len-ansnow.y-1,len-ansnow.x-1}; } int main() { freopen("in.txt","r",stdin); int T; rd(T); while(T--) { rd(N),rd(a),rd(b); pa=dfs(a-1,N); pb=dfs(b-1,N); double x=pa.x-pb.x; double y=pa.y-pb.y; double ans=sqrt(x*x+y*y); printf("%.0lf\n",ans*10); } re 0; }
0x03前缀和与差分
1.二维前缀和计算
2.差分化区间修改为单点修改
1.IncDec Sequence
话说这道题是紫题,您认真的吗
#include#define re return #define ll long long #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } ll n,x,last,cnt1,cnt2; int main() { rd(n); rd(last); inc(i,2,n) { rd(x); if(x-last<0)cnt2+=last-x; else cnt1+=x-last; last=x; } printf("%lld\n",max(cnt1,cnt2)); printf("%lld",abs(cnt1-cnt2)+1); re 0; }
2.区间统计Tallest Cow
#include#define re return #define ll long long #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } map int,int>,bool>existed; int n,m,p,h,d[10010]; int main() { freopen("in.txt","r",stdin); int x,y; rd(n),rd(p),rd(h),rd(m); inc(i,1,m) { rd(x),rd(y); if(x>y)swap(x,y); if(existed[make_pair(x,y)])continue; d[x+1]--; d[y]++; existed[make_pair(x,y)]=1; } int ans=0; inc(i,1,n) { ans+=d[i]; printf("%d\n",ans+h); } re 0; }
0x04二分
1.整数二分
2.实数二分
3.三分
4.二分答案化判定
我就是看到有一道交互题从乡下刚来城市什么见识,就去水了一道
真好玩~
1.特殊排序
// Forward declaration of compare API. // bool compare(int a, int b); // return bool means whether a is less than b. class Solution { public: vector<int> specialSort(int N) { vector<int>v; v.push_back(1); for(int i=2;i<=N;++i) { int l=0,r=v.size()-1; while(l<=r) { int mid=(l+r)>>1; if(compare(v[mid],i))l=mid+1; else r=mid-1; } v.insert(v.begin()+l,i); } return v; } };
0x05排序
1.离散化
2.中位数
奇数(n+1)>>1
偶数n/2~n/2+1
0x06倍增
1.基本
genius ACM
2.st算法
0x07贪心
总而言之,就是让你用一种简单的排序或复杂的推式子得到一种最优的贪心celue
防晒
0x10 基本数据结构
0x11 栈
1.对顶栈
请参见对顶堆,不过堆可能要维护大小之类的东西
编辑器
2.与Catalan数的不解之缘
火车进出栈问题
3.表达式求值
反正中缀转后缀O(n)
前后缀直接算
表达式转换
4.单调栈
rt
0x12 队列
1.
a.小组队列
模拟
b.蚯蚓
维护3个单调递减的队列
c.双端队列
从结果出发,排序,按原标号维持先递减后递增的最少块数
2.单调队列
最大子序和
0x13链表与邻接表
邻值查找
模拟维护存在性
#include#define re return #define dec(i,l,r) for(int i=l;i>=r;--i) #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9') x=x*10+(c^48); if(f)x=-x; } const int maxn=100005; int n,hide[maxn],L[maxn],R[maxn],vis[maxn]; struct ndoe { int val,pos; }ans[maxn]; struct node { int val,id; inline bool operator<(node a)const { re val<a.val; } }a[maxn]; int main() { // freopen("in.txt","r",stdin); rd(n); inc(i,1,n) { rd(a[i].val); a[i].id=i; } sort(a+1,a+n+1); inc(i,1,n) { vis[i]=1; hide[a[i].id]=i; L[i]=i-1; R[i]=i+1; } dec(now,n,2) { int i=hide[now]; int l=L[i],r=R[i],minn=0x3f3f3f3f; while(l&&vis[l]==0)l=L[i]; while(r&&vis[r]==0)r=R[r]; if(l&&a[i].val-a[l].val<minn) { minn=abs(a[l].val-a[i].val); ans[a[i].id].pos=a[l].id; } if(r&&a[r].val-a[i].val<minn) { minn=a[r].val-a[i].val; ans[a[i].id].pos=a[r].id; } ans[a[i].id].val=minn; R[l]=r; L[r]=l; vis[i]=0; } inc(i,2,n) printf("%d %d\n",ans[i].val,ans[i].pos); re 0; }
0x30 Math
其实数学只要就是推理过程
0x31质数
1.质数的判定(试除法||miller_robbin)
2.质数的筛选
eratosthenes筛法
线性筛法
2.质因数的分解
算术基本定理
试除法
质数距离
阶乘分解
0x32约数
1.算术基本定理推论
2.试除法推论
3.倍数法及其推论
反素数
见一本通
余数之和
#include#define re return #define ll long long #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } ll n,k,gx; ll ans; int main() { rd(n);rd(k); ans=n*k; for(int x=1;x<=n;x=gx+1) { gx=k/x?min(k/(k/x),n):n; ans-=(k/x)*(gx-x+1)*(x+gx)/2; } printf("%lld",ans); re 0; }
4.最大公约数
5.更相减损术
6.欧几里得法
Hankson的趣味题
7.互质与欧拉函数
8.积性函数
可见的点
0x33同余
1.同余类以及剩余类
2.费马小定理
3.欧拉定理及推论
最幸运的数字
#include#define re return #define ll long long #define inc(i,l,r) for(ll i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } ll n; inline int gcd(ll a ,ll b){re b?gcd(b,a%b):a;} inline int Get_phi(ll x) { ll ans=x; ll largest=sqrt(x); for(int i=2;i<=largest;++i) if(x%i==0) { ans=ans*(i-1)/i; while(x%i==0) x=x/i; } if(x!=1) ans=(ans)*(x-1)/x; re (ll)ans; } inline ll pow(ll a,ll x,ll mod) { ll ret=1; while(x) { if(x&1)ret=(ret*a)%mod; a=(a*a)%mod; x>>=1; } re ret; } int main() { // freopen("in.txt","r",stdin); int cnt=0; while(2333) { ++cnt; rd(n); if(!n)break; ll mod=n*9/gcd(8,n); if(gcd(mod,10)!=1){ printf("Case %d: 0\n",cnt); continue; } ll ol=Get_phi(mod); ll ans=9999999999999999; inc(i,1,sqrt(ol)) { if(ol%i)continue; if(pow(10,i,mod)==1)ans=min(ans,i); if(pow(10,ol/i,mod)==1)ans=min(ans,ol/i); } printf("Case %d: %lld\n",cnt,ans); } re 0; }
4.扩展欧几里得
5.裴蜀定理
6.乘法逆元
7.线性同余方程
同余方程
#include#define re return #define ll long long #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } inline ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1;y=0;//小心越界 re a; } ll d=exgcd(b,a%b,x,y); ll z=x; x=y; y=z-(a/b)*y; re d; } int main() { ll a,b,x,y; rd(a),rd(b); exgcd(a,b,x,y); printf("%lld",(x%b+b)%b); re 0; }
8.中国剩余定理
表达整数的奇怪方式
9.高次同余方程(大步小步算法)
0x34矩阵乘法
将线性式横摆
若状态矩阵中第x个数对下一单位时间状态矩阵中的第y个数有影响,
则转移矩阵中的第x行第y列要赋予恰当的系数
斐波那契
石头游戏
#include#define re return #define ll long long #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=65; ll n,m,act,t,ym; ll opt[10][10],num[10][10],f[65]; char s[10][10]; struct node{ ll a[65][65]; inline void pre() { inc(i,0,ym)inc(j,0,ym) a[i][j]=0; } inline void Identity() { inc(i,0,ym)a[i][i]=1; } inline node operator*(node c) { node d; d.pre(); inc(i,0,ym)inc(j,0,ym)inc(k,0,ym) d.a[i][j]+=a[i][k]*c.a[k][j]; re d; } }J[61],B; inline void muls(ll u[65],node L) { ll w[65];memset(w,0,sizeof(w)); for(int j=0;j<=ym;j++) for(int k=0;k<=ym;k++) w[j]+=u[k]*L.a[k][j]; memcpy(u,w,sizeof(w)); } inline void mul(ll x) { while(x) { if(x&1)muls(f,B); B=B*B; x>>=1; } } int main() { //freopen("in.txt","r",stdin); scanf("%lld%lld%lld%lld",&n,&m,&t,&act); ym=n*m; char c; inc(i,1,n)inc(j,1,m) { while((c=getchar())<'0'||c>'9'); opt[i][j]=c-48; num[i][j]=(i-1)*m+j; } inc(i,1,act) scanf("%s",s[i-1]+1); inc(i,1,n)inc(j,1,m) { ll now=opt[i][j],nowx=0,len=strlen(s[now]+1); inc(k,1,60) { J[k].a[0][0]=1; if(++nowx>len)nowx=1; switch(s[now][nowx]) { case 'N':if(i>1)J[k].a[num[i][j]][num[i-1][j]]=1;break; case 'S':if(i 1][j]]=1;break; case 'W':if(j>1)J[k].a[num[i][j]][num[i][j-1]]=1;break; case 'E':if(j 1]]=1;break; case 'D':break; } if('0'<=s[now][nowx]&&s[now][nowx]<='9') J[k].a[num[i][j]][num[i][j]]=1,J[k].a[0][num[i][j]]=s[now][nowx]-48; } } B.pre();B.Identity(); inc(i,1,60) B=B*J[i]; f[0]=1; ll w=t/60; mul(w); w=t%60; inc(i,1,w) muls(f,J[i]); ll ans=0; inc(i,1,ym) ans=max(ans,f[i]); printf("%lld",ans); re 0; }
0x35高斯消元与线性空间
1.高斯消元
球形空间产生器
开关问题
2.线性空间
装备购买
0x36组合计数
1.加法原理
2.乘法原理
3.排列与组合数
4.二项式定理
计算系数
二项式定理
5.多重集的排列与组合
计数交换
神神奇奇的转化+莫名其妙的多重集组合数
6.lucas定理
古代猪文
欧拉定理+卢卡斯+crt
好题(虽然很基础)
7.catalan数列
0x38 概率与数学期望
大致就是让你找到一个期望值位1的状态,然后推到每一维状态
Rainbow的信号
已知枚举区间的总期望值为1
可知你每次选的了l,r本身就是概率
又由于位运算没有进位的限制
所以我们针对每一位运算,每次O(n)枚举右区间r
#include#define re return #define dec(i,l,r) for(int i=l;i>=r;--i) #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=100005; int n,a[maxn],b[maxn],last[2]; #define D double D ans_or,ans_xor,ans_and; inline void work(int k) { D sum=1< 0; //sum是这一位原本的贡献(basic) //所以答案累计时要乘上去 inc(i,1,n) { b[i]=(a[i]>>k)&1; if(b[i])ans_now+=sum*1.0/n/n; //区间为1的答案对于三个ans都有贡献 //我就是懒~ } ans_or+=ans_now; ans_xor+=ans_now; ans_and+=ans_now; last[0]=last[1]=0; //上一次0,1,出现位置 int c1=0,c2=0; //出现1的个数和为对当前有贡献的个数c2 //无贡献的个数c1 inc(i,1,n) { if(!b[i])//如果这位不是1 ans_or+=sum*last[1]*2.0/n/n; //异或和才有值=》第一位到最后一次出现1的位子为左边界 else //如果是1 { ans_or+=sum*(i-1)*2.0/n/n;//左边界任意 ans_and+=sum*(i-1-last[0])*2.0/n/n;//必须是连续的一段1的左边界 } ans_xor+=sum*2.0*(b[i]?c1:c2)/n/n; //当前数要异或偶数个1,还是奇数个1才能使当前值为1 ++c1;//累加当前数统计异或数(当前数与当前数异或肯定为0) if(b[i])swap(c1,c2);//如果当前数为1,要异或个数的1就要轮转 last[b[i]]=i; } } int main() { //freopen("in.txt","r",stdin); rd(n); inc(i,1,n)rd(a[i]); inc(i,0,30)//最多只有26位,但反正又不超,保险 work(i); printf("%.3lf %.3lf %.3lf",ans_xor,ans_and,ans_or); re 0; }
绿豆蛙的归宿
只说了1可以到达任意位置,又没说1一定达到n
不如设到达n的期望为一
反向建图
#include#define re return #define dec(i,l,r) for(int i=l;i>=r;--i) #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } typedef double D; const int maxn=100005; D f[maxn]; int n,m,k,in[maxn],hd[maxn],son[maxn]; struct node{ int to,nt; D val; }e[maxn<<1]; inline void add(int x,int y,int z) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z; } inline void topo() { queue<int>q; q.push(n); f[n]=0; while(!q.empty()) { int x=q.front(); if(x==1)re; q.pop(); for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; f[v]+=1.0*(f[x]+e[i].val)/son[v]; --in[v]; if(!in[v]) q.push(v); } } } int main() { // freopen("in.txt","r",stdin); int x,y,z; rd(n),rd(m); inc(i,1,m) { rd(x),rd(y),rd(z); add(y,x,z); ++in[x]; ++son[x]; } topo(); printf("%.2lf",f[1]); re 0; }
0x40 数据结构的膜法变身
0x41 曾经,我也只是一只单纯的并查集
1.路径压缩与按秩合并
如果你学过并查集的话,那么你一定感慨曾见过只穿了路径压缩的冰茶姬
所谓按秩合并就是启发式(小入大)
题:程序自动分析
2.扩展域与边带权
所谓扩展域,也可以理解为一种容易实现的边带权
(以带权的奇偶性来表示是否在同一集合)
不过看来,边带权的可运用性更加广泛
看书,有笔记
0x41 震惊!线段树竟然有一个血缘关系高达99%
的亲戚——短小精悍的树状数组
1.单点修改,区间求和
2.求逆序对
楼兰图腾
常规做法
3.in combination with 查分
区间修改,单点查询
一个简单的整数问题
区间修改,区间查询
一个简单的整数问题2
你要推一波式子
4.else
谜一样的牛
首先想到模拟,然后用二分或倍增优化
0x42 你从未见过的船新版本
1.区间最大公约数
明明就只是弄个差分,取个绝对值,在球球gcd
为什么我kao了2天
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T &s) { s = 0; T w = 1, ch = getchar(); while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); } while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); } s *= w; } #define ll long long #define lowbit(x) (x&(-x)) const long long maxn=5e5+5; ll n,m; ll a[maxn],c[maxn],b[maxn]; inline void add(int pos,ll x) { for(;pos<=n;pos+=lowbit(pos)) c[pos]+=x; } inline ll sum(int pos) { ll ret=0; for(;pos;pos-=lowbit(pos)) ret+=c[pos]; re ret; } struct tree { ll l,r,val; }t[maxn<<2]; #define ls rt<<1 #define rs rt<<1|1 ll gcd(ll a,ll b){b?gcd(b,a%b):a;} inline void build(int rt,int l,int r) { t[rt].l=l;t[rt].r=r; if(l==r) { t[rt].val=b[l]; re; } int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); t[rt].val=gcd(t[ls].val,t[rs].val); } inline void addt(ll rt,ll pos,ll x) { if(t[rt].l==t[rt].r) { t[rt].val+=x; re ; } ll mid=(t[rt].l+t[rt].r)>>1; if(pos<=mid)addt(ls,pos,x); else addt(rs,pos,x); t[rt].val=gcd(t[ls].val,t[rs].val); } inline ll query(ll rt,ll x,ll y) { if(x<=t[rt].l&&t[rt].r<=y) re abs(t[rt].val); ll mid=(t[rt].l+t[rt].r)>>1; ll ans1=0,ans2=0; if(x<=mid)ans1=query(ls,x,y); if(y>mid) ans2=query(rs,x,y); re abs(gcd(ans1,ans2)); } int main() { cin>>n>>m; inc(i,1,n) { scanf("%lld",&a[i]); b[i]=a[i]-a[i-1]; } build(1,1,n); ll x,y,z; char opt[4]; inc(i,1,m) { scanf("%s",opt); scanf("%lld%lld",&x,&y); if(opt[0]=='C') { scanf("%lld\n",&z); if(y 1,y+1,-z); add(x,z);addt(1,x,z); add(y+1,-z); } else { scanf("\n"); ll ans1=a[x]+sum(x),ans2=0; if(x 1,x+1,y); printf("%lld\n",gcd(ans1,ans2)); } } re 0; }
0x50 DP大法吼啊
0x51 线性dp
我就是个lj
都是基础的dp+一些小优化
杨老师的照相排列
最长公共上升子序列
分级
还有一些
自己跳过去看吧
0x52 背包
1.0/1背包
滚动数组
倒序
2.完全背包
0/1正序就是
3.多重背包
朴素不如二进制 ,二进制不如单调队列
4.分组背包
某种形式上的0/1
陪审团
多维0/1背包,考虑适当优化
0x53 区间dp
1.石子合并 欧阳举了无数次例子的题
2.Polygon
比较常规的区间dp了
要注意转移时负负得正等运算
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=105; int n,f[101][101][2],opt[maxn],a[maxn]; inline void swup(int &x,int &y) { x^=y^=x^=y; } int main() { // freopen("in.txt","r",stdin); rd(n); inc(i,1,n<<1)inc(j,1,n<<1) { f[i][j][1]=-6666666; f[i][j][0]=77777777; } char op[5]; inc(i,1,n) { scanf("%s",op); rd(a[i]); a[i+n]=a[i]; f[i][i][0]=f[i][i][1]=a[i]; f[i+n][i+n][0]=f[i+n][i+n][1]=a[i]; opt[i]=(op[0]=='t');//为加是true opt[i+n]=opt[i]; } int max1,min1,max2,min2; inc(L,1,n-1) inc(i,1,n+n-L) { int j=i+L; inc(k,i+1,j) if(opt[k])//加 { f[i][j][1]=max(f[i][j][1],f[i][k-1][1]+f[k][j][1]); f[i][j][0]=min(f[i][j][0],f[i][k-1][0]+f[k][j][0]); } else { max1=f[i][k-1][1]*f[k][j][1],min1=f[i][k-1][0]*f[k][j][0]; if(max1<min1)swup(max1,min1); max2=f[i][k-1][1]*f[k][j][0],min2=f[i][k-1][0]*f[k][j][1]; if(max2<min2)swup(max2,min2); f[i][j][1]=max(f[i][j][1],max(max1,max2)); f[i][j][0]=min(f[i][j][0],max(min1,min2)); } } int ans=-5555555,cnt; int q[maxn]; inc(i,1,n) if(f[i][i+n-1][1]>ans) { ans=f[i][i+n-1][1]; q[cnt=1]=i; } else if(ans==f[i][i+n-1][1]) q[++cnt]=i; printf("%d\n",ans); inc(i,1,cnt) printf("%d ",q[i]); re 0; }
3.金字塔
要判断每棵子树进出点应该是同一个数,才行
#include#define ll long long #define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } ll n,f[305][305],mod=1000000000; char s[305]; //为什么只用枚举第一棵子树呢,一个是防重,也方便统计 inline ll dfs(int l,int r) { if(s[l]!=s[r])re 0; if(l==r)re 1; if(f[l][r]!=-1)re f[l][r]; f[l][r]=0; inc(k,l+2,r) f[l][r]=(f[l][r]+dfs(l+1,k-1)*dfs(k,r)%mod)%mod; re f[l][r]; } int main() { // freopen("in.txt","r",stdin); memset(f,-1,sizeof f); scanf("%s",s+1); printf("%lld",dfs(1,strlen(s+1))); re 0; }
0x54 树形dp
1.基础dp
由根分为左右子树两部分情况
二叉苹果树
树的最长链
数字转换
树的最大独立子集
战略游戏
普通树的dp
跟他的儿子与父亲有着不可告人的关系
皇宫看守
2.背包类树形dp
分组背包
选课
3.换根与二次扫描法
对于不定根求解
Accumulation Degree
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=200005; int n,m,k,hd[maxn],in[maxn],D[maxn],f[maxn]; struct node{ int to,nt,val; }e[maxn<<1]; inline void add(int x,int y,int z) { ++in[x];++in[y]; e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z; e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=z; } inline void dfs(int x,int fa) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa)continue; dfs(v,x); if(in[v]==1)D[x]+=e[i].val; else D[x]+=min(D[v],e[i].val); } } inline void dfs1(int x,int fa) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa)continue; if(in[x]==1)f[v]=D[v]+e[i].val; else f[v]=D[v]+min(f[x]-min(D[v],e[i].val),e[i].val); dfs1(v,x); } } int main() { //freopen("in.txt","r",stdin); int T,x,y,z; rd(T); while(T--) { rd(n); k=0; inc(i,1,n)hd[i]=D[i]=f[i]=in[i]=0; inc(i,2,n) { rd(x),rd(y),rd(z); add(x,y,z); } dfs(1,1); f[1]=D[1]; dfs1(1,1); int ans=0; inc(i,1,n) if(f[i]>f[ans]) ans=i; printf("%d\n",f[ans]); } re 0; }
0x55 环形与后效性处理
1.环形
要么开二倍拆链
要么强制选或不选
a.Naptime
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=4000; int n,m,b,ans,f[maxn][maxn][2],val[maxn]; inline void dp() { inc(now,2,n) { int i=now&1; inc(j,0,min(now-1,m)) { f[i][j][0]=max(f[i^1][j][0],f[i^1][j][1]); if(j)f[i][j][1]=max(f[i^1][j-1][0],f[i^1][j-1][1]+val[now]); } } } inline void pre() { inc(i,0,1) inc(j,0,m) f[i][j][0]=f[i][j][1]=-1147483647; } int main() { //freopen("in.txt","r",stdin); rd(n),rd(m); inc(i,1,n)rd(val[i]); pre(); f[1][0][0]=f[1][1][1]=0; dp(); ans=max(f[n&1][m][1],f[n&1][m][0]); pre(); f[1][1][1]=val[1];//因为会强制不选 dp(); ans=max(ans,f[n&1][m][1]); printf("%d",ans); re 0; }
b.环路运输
也可以不拆,但拆了更简单
单调队列
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=1e6+5; int n,a[maxn<<1],ans; int q[maxn<<1]; int main() { freopen("in.txt","r",stdin); rd(n); inc(i,1,n) { rd(a[i]); a[i+n]=a[i]; } int nn=n/2; int tail=0,head=1; inc(i,1,n<<1) { if(head<=tail&&i-q[head]>nn)++head; ans=max(ans,a[q[head]]+a[i]+i-q[head]); while(head<=tail&&a[i]-i>=a[q[tail]]-q[tail])--tail; q[++tail]=i; } printf("%d",ans); re 0; }
2.有后效性
0x57 倍增优化dp
1.预处理
数据小,易处理
2.倍增
常数极大
3.求解
开车旅行
计算重复
为什么这道题不能有scanf读入
#include#define re return #define ll long long #define dec(i,l,r) for(ll i=l;i>=r;--i) #define inc(i,l,r) for(ll i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=105; ll n1,n2,l1,l2,f[maxn][32]; char s1[105],s2[105]; int main() { // freopen("in.txt","r",stdin); while(cin>>s2>>n2>>s1>>n1) { l1=strlen(s1); l2=strlen(s2); //PRE int flag=0; inc(i,0,l1-1) { f[i][0]=0; ll pos=i; inc(j,0,l2-1) { ll cnt=0; while(s1[pos]!=s2[j]) { pos=(1+pos)%l1; if(++cnt>l1+1) { flag=1; break; } } if(flag) break; pos=(1+pos)%l1;//下一位开始 f[i][0]+=cnt+1;//往后累加一位 } if(flag)break; } if(flag) printf("0\n"); else { //倍增 inc(i,0,30) inc(j,0,l1-1) f[j][i+1]=f[j][i]+f[(j+f[j][i])%l1][i]; ll m=0; /* inc(j,0,l1-1) {*/ ll pos=0,cnt=0; dec(i,31,0) if(pos+f[pos%l1][i]<=n1*l1) { pos+=f[pos%l1][i]; cnt+=1<<i; } m=max(m,cnt); // } printf("%lld\n",m/n2); } } }
0x60 tulun
0x61 最短路
1.单源最短路
dijkstra+spfa+bellman-ford
通信线路
最优贸易
正反两遍spfa
道路与航线
分开处理
2.floyd
任意两点最短路
传递闭包
排序
传递关系
观光之旅
longlong memset赋0x3f3f3f3f(八位字节会爆)
牛站
矩阵最短路???
0x62最小生成树kuskal+prim
由于最小生成树太水了,所以总要结合一些奇奇怪怪的算法
走廊泼水节
累和型dp
野餐规划
强制选择型dp
沙漠之王
0/1分数型dp
黑暗城堡
计数型dp
0x63 树的直径与最近公共祖先
1.树的直径
巡逻
竟然恶心的要用2次dfs(无法求负权)与树形dp(无法求路径)
分别求一次树的直径
#include#define re return #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=200005; int n,m,k=1,ans,total,K,hd[maxn],d[maxn],deep,fa[maxn]; struct node{ int to,nt,val; }e[maxn<<1]; inline void add(int x,int y) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=1; e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=1; } inline void dfs(int x) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; fa[v]=x; d[v]=d[x]+e[i].val; if(d[deep] v; dfs(v); } } inline void dfs1(int x) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x]) { e[i].val=e[i^1].val=-1; dfs1(v); re; } } } inline void Get_long() { deep=0; d[1]=0;fa[1]=1; dfs(1); int rt=deep; deep=0; d[rt]=0;fa[rt]=rt; dfs(rt); } inline void dfs2(int x) { int mson=0; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; d[v]=0; fa[v]=x; dfs2(v); d[v]+=e[i].val; ans=max(ans,d[v]+d[mson]); if(d[mson] v; } d[x]=max(0,d[mson]); } int main() { // freopen("in.txt","r",stdin); rd(n),rd(K); int x,y; inc(i,2,n) { rd(x),rd(y); add(x,y); } total=n*2-2; Get_long(); total=total-d[deep]+1; if(K==1); else { dfs1(deep); d[1]=0;fa[1]=1; dfs2(1); total=total-ans+1; } printf("%d",total); re 0; }
树网的核
noip n=500的数据之水,n^3都阔以
#include#define re return #define Min(a,b) (a)<(b)?(a):(b) #define Max(a,b) (a)>(b)?(a):(b) #define inc(i,l,r) for(register int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=500005; int n,m,S,k,ans=0x3f3f3f3f,deep; int hd[maxn],dis[maxn],d[maxn],fa[maxn],vis[maxn]; struct node{ int to,nt,val; }e[maxn<<1]; inline void add(int x,int y,int z) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z; e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=z; } inline void dfs(int x) { if(d[deep] x; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; fa[v]=x; d[v]=d[x]+e[i].val; dfs(v); } } inline void dfs1(int x) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; dfs1(v); if(!vis[v])dis[x]=max(dis[x],dis[v]+e[i].val); } } inline void Get_long() { dfs(1); int rt=deep; deep=0; fa[rt]=0; d[rt]=0; dfs(rt); for(int i=deep,j=deep;i;i=fa[i]) { vis[i]=1; while(j&&d[i]-d[fa[j]]<=S)j=fa[j]; ans=min(ans,max(d[deep]-d[i],d[j])); } d[rt]=0; dfs1(rt); for(int i=deep;i;i=fa[i]) ans=max(ans,dis[i]); } int main() { // freopen("in.txt","r",stdin); int x,y,z; rd(n),rd(S); inc(i,2,n) { rd(x),rd(y),rd(z); add(x,y,z); } Get_long(); printf("%d",ans); re 0; }
你看看bzoj n=500000
我巨大的常数都卡不了
#include#define re return #define Min(a,b) (a)<(b)?(a):(b) #define Max(a,b) (a)>(b)?(a):(b) #define inc(i,l,r) for(register int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=500005; int n,m,S,k,ans=0x3f3f3f3f,deep; int hd[maxn],dis[maxn],d[maxn],fa[maxn],vis[maxn]; struct node{ int to,nt,val; }e[maxn<<1]; inline void add(int x,int y,int z) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z; e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=z; } inline void dfs(int x) { if(d[deep] x; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; fa[v]=x; d[v]=d[x]+e[i].val; dfs(v); } } inline void dfs1(int x) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa[x])continue; dfs1(v); if(!vis[v])dis[x]=max(dis[x],dis[v]+e[i].val); } } inline void Get_long() { dfs(1); int rt=deep; deep=0; fa[rt]=0; d[rt]=0; dfs(rt); for(int i=deep,j=deep;i;i=fa[i]) { vis[i]=1; while(j&&d[i]-d[fa[j]]<=S)j=fa[j]; ans=min(ans,max(d[deep]-d[i],d[j])); } d[rt]=0; dfs1(rt); for(int i=deep;i;i=fa[i]) ans=max(ans,dis[i]); } int main() { freopen("in.txt","r",stdin); int x,y,z; rd(n),rd(S); inc(i,2,n) { rd(x),rd(y),rd(z); add(x,y,z); } Get_long(); printf("%d",ans); re 0; }
2.lca
3.差分
暗的连锁
附加边产生环
对附加边差分(塞到两点里)
代表从他父亲到他的主要边被几个环覆盖
0个环任意切第二刀
1环绑定切一刀
其他不成立
#include#define re return #define dec(i,l,r) for(int i=l;i>=r;--i) #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); } const int maxn=1e5+5; int n,m,k,hd[maxn<<1],d[maxn],ans,fa[maxn][25],deep[maxn]; struct node{ int to,nt; }e[maxn<<1]; inline void add(int x,int y) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k; e[++k].to=x;e[k].nt=hd[y];hd[y]=k; } inline void dfs(int x,int pre) { deep[x]=deep[pre]+1; for(int i=0;fa[fa[x][i]][i];++i) fa[x][i+1]=fa[fa[x][i]][i]; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==pre)continue; fa[v][0]=x; dfs(v,x); } } inline int LCA(int x,int y) { if(deep[x]<deep[y])swap(x,y); dec(i,20,0) if(deep[fa[x][i]]>=deep[y]) x=fa[x][i]; if(x==y)re x; dec(i,20,0) if(fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } re fa[x][0]; } inline void Get_ans(int x,int pre) { for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==pre)continue; Get_ans(v,x); d[x]+=d[v]; } if(!pre)re ; if(d[x]==1)++ans; if(!d[x])ans+=m; } int main() { //freopen("in.txt","r",stdin); int x,y; rd(n),rd(m); inc(i,2,n) { rd(x),rd(y); add(x,y); } dfs(1,0); inc(i,1,m) { rd(x),rd(y); int v=LCA(x,y); ++d[x]; ++d[y]; d[v]-=2; } Get_ans(1,0); printf("%d",ans); re 0; }
雨天的尾巴
天天爱跑路
4.lca的综合应用
异象石
set应用
次小生成树
dp,dp还是dp
疫情控制
stl,dp
0x65负环与差分约束
1.负环
观光奶牛
熟悉的味道,熟悉的配方
是的,0/1分数规划
2.差分约束
区间