# N数码判定性问题

1 2 3
4 5 6
7 0 8转化为1 2 3 4 5 6 7 8

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
int n,a[265000],b[265000];
int s[265000];
int lowbit(int x){return x&-x;}
void change(int x){while(x<=n*n-1)s[x]++,x+=lowbit(x);}
int findsum(int x){int ret=0;while(x){ret+=s[x],x-=lowbit(x);}return ret;}
int main()
{
//	freopen("a.in","r",stdin);
while(scanf("%d",&n)!=EOF)
{
int lx=0,ly=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x;scanf("%d",&x);
if(x!=0)a[++lx]=x;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x;scanf("%d",&x);
if(x!=0)b[++ly]=x;
}
memset(s,0,sizeof(s));
LL sx=0,sy=0;
for(int i=lx;i>=1;i--){sx+=(LL)findsum(a[i]);change(a[i]);}
memset(s,0,sizeof(s));
for(int i=ly;i>=1;i--){sy+=(LL)findsum(b[i]);change(b[i]);}
if((sx%2)==(sy%2))printf("TAK\n");
else printf("NIE\n");
}
return 0;
}


## 倍增替代二分

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL k;int n,m,a[510000];
LL b[510000],c[510000],d[510000],G[510000];
inline LL sqr(LL x){return x*x;}
void megsort(int l,int r)
{
if(l>=r)return ;
int mid=(l+r)/2;
megsort(l,mid);megsort(mid+1,r);
int t=0,u=l,v=mid+1;
while(u<=mid && v<=r)
{
if(c[u]<=c[v])d[++t]=c[u++];
else d[++t]=c[v++];
}
while(u<=mid)d[++t]=c[u++];
while(v<=r)d[++t]=c[v++];
for(int i=1;i<=t;i++)c[i+l-1]=d[i];
}
bool check(int S,int st,int ed)
{
int len=0,lx=(st-1)-S+1;
for(int i=st;i<=ed;i++)c[++len]=a[i];
megsort(1,len);
int t=0,u=1,v=1;
while(u<=lx && v<=len)
{
if(b[u]<c[v])d[++t]=b[u++];
else d[++t]=c[v++];
}
while(u<=lx)d[++t]=b[u++];
while(v<=len)d[++t]=c[v++];
for(int i=1;i<=t;i++)G[i]=d[i];
len=ed-S+1;
LL sum=0;u=min(m,len/2);
for(int i=1;i<=u;i++)sum+=sqr(G[i]-G[len-i+1]);
if(sum<=k)
{
for(int i=1;i<=t;i++)b[i]=G[i];
return true;
}
return false;
}
int main()
{
//	freopen("a.in","r",stdin);
int TT;scanf("%d",&TT);
while(TT--)
{
scanf("%d%d%lld",&n,&m,&k);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
int ans=0,s=1,t=1,p=1;b[1]=a[1];
while(t<n)
{
if(p==0){ans++;s=++t;b[1]=a[s];p=1;continue;}
if(check(s,t+1,min(t+p,n)))
{
t+=p,p*=2;
if(t>n){ans++;break;}
}
else p/=2;
}
if(t==n)ans++;
printf("%d\n",ans);
}
return 0;
}


## 最小表示法

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
char ch[210000];
int main()
{
scanf("%s",ch+1);int len=strlen(ch+1);
for(int i=len+1;i<=2*len;i++)ch[i]=ch[i-len];
int i=1,j=2,k;
while(i<=len && j<=len)
{
for(k=0;ch[i+k]==ch[j+k] && k<=len;k++);
if(k==len)break;
if(ch[i+k]-'0'<ch[j+k]-'0')
{
j=j+k+1;
if(j==i)j++;
}
else
{
i=i+k+1;
if(i==j)i++;
}
}
printf("%d\n",min(i,j));
return 0;
}


## 哈夫曼树

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
struct node
{
int meg;LL c;
node(){meg=0;c=0;}
friend bool operator <(node n1,node n2)
{
if(n1.c!=n2.c)return n1.c>n2.c;
return n1.meg>n2.meg;
}
};
priority_queue<node> q;
int n,k;
LL w[210000];
node cnt[15];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
while((n-1)%(k-1))w[++n]=0;
for(int i=1;i<=n;i++)
{
node tmp;tmp.c=w[i];tmp.meg=0;
q.push(tmp);
}
LL ans=0;
while(q.size()!=1)
{
for(int i=1;i<=k;i++)cnt[i]=q.top(),q.pop();
node tmp;tmp.meg=0;tmp.c=0;
for(int i=1;i<=k;i++)tmp.c+=cnt[i].c,tmp.meg=max(tmp.meg,cnt[i].meg);
tmp.meg++;ans+=tmp.c;
q.push(tmp);
}
node tmp=q.top();
printf("%lld\n%d\n",ans,tmp.meg);
return 0;
}


## 线段树扫描线

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int n,tim;
//LS
double w[1100];int tlen;
struct edge{double l,r,x;int k;}a[1100];//上坐标 下坐标 x坐标
bool cmp(edge n1,edge n2){return n1.x<n2.x;}
int fd(double num)
{
int l=1,r=tlen,ret;
while(l<=r)
{
int mid=(l+r)/2;
if(w[mid]<num)l=mid+1;
else r=mid-1,ret=mid;
}
return ret;
}
//seg
int cover[1100];
double sum[1100];
void pushup(int now,int l,int r)
{
if(cover[now])sum[now]=w[r]-w[l];
else if(l+1==r)sum[now]=0;
else sum[now]=sum[now<<1]+sum[now<<1|1];
}
void change(int now,int l,int r,int ql,int qr,int c)
{
if(l==ql && r==qr)
{
cover[now]+=c;
pushup(now,l,r);
return ;
}
int mid=(ql+qr)/2;
if(r<=mid)change(now<<1,l,r,ql,mid,c);
else if(mid<=l)change(now<<1|1,l,r,mid,qr,c);
else
{
change(now<<1,l,mid,ql,mid,c);
change(now<<1|1,mid,r,mid,qr,c);
}
pushup(now,ql,qr);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
tim++;tlen=0;
memset(cover,0,sizeof(cover));
memset(sum,0,sizeof(sum));
if(!n)break;
for(int i=1;i<=n;i++)
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
a[2*i-1].l=a[2*i].l=y1;a[2*i-1].r=a[2*i].r=y2;
a[2*i-1].x=x1;a[2*i].x=x2;
a[2*i-1].k=1;a[2*i].k=-1;
w[++tlen]=y1;w[++tlen]=y2;
}
sort(a+1,a+1+2*n,cmp);
sort(w+1,w+1+tlen);
tlen=unique(w+1,w+1+tlen)-(w+1);
n*=2;
double ans=0;
a[0].x=a[1].x;
for(int i=1;i<=n;i++)
{
ans+=(a[i].x-a[i-1].x)*sum[1];
double upper=fd(a[i].l),downer=fd(a[i].r);
change(1,upper,downer,1,tlen,a[i].k);
}
printf("Test case #%d\n",tim);
printf("Total explored area: %.2lf \n\n",ans);
}
return 0;
}


lyd告诉我还有一种方法

##有后效性的dp

## 单调队列与斜率优化

$f[i]=min(f[j]+val(j,i))$

$\frac {Y(i)-Y(j)}{X(i)-X(j)}$

## 四边形不等式

$f[p[i]]+val(p[i],i)<=f[j]+val(j,i)$

$val(j,i)+val(p[i],k)<=val(j,k)+val(p[i],i)$

$val(p[i],k)-val(p[i],i)<=val(j,k)-val(j,i)$
1式与3式相加有
$f[p[i]]+val(p[i],k)<=f[j]+val(j,k)$

f具有决策单调性

## 搜索的奇技淫巧

1：有一个叫做bitset的东西可以$/32$的常数，转化二进制的时候多想想这个
2：直接$2^n$判定性问题搜过不去的时候，不妨试一试折半搜索。前一半正常搜出答案并记录排序去重，后一半搜出答案后二分与第一段搜出的答案合并求解，复杂度大约可以降为$2^{\frac{n}{2}}+2^{\frac{n}{2}}*\log{\frac{n}{2}}$
3：从大到小或者从小到大排序后与当前答案比较大小搜索剪枝会有奇效谁试谁知道
4：对于拼接性问题可以选择排除冗杂状态，比如限制加入的数是从大到小的，因为先加x再加y和先加y再加x的搜索分支是相同的
5：毫无头绪的时候不妨推推柿子
6：在能确定答案较小的时候，可以通过限制搜索的深度来减少不必要的较深的搜索过程
7：如果不能使BFS序列值单调不降的时候，试试合并状态或者多套一个BFS？

## 线性空间与高斯消元

1：向量加法：a+b其中a，b均为向量
2：标量乘法：k*a其中k为常数，a为向量

m的矩阵看作一个系数矩阵进行高斯消元，消元后不全为0的行向量线性无关，因为高斯消元的原理就是向量加法和标量乘法。所以这些行向量可以被称为上面这个线性空间的基

## 卡特兰数

n辆火车进出栈问题的答案等于卡特兰数的第n项
n个左括号和n个右括号组成的合法括号序列的数量为卡特兰数的n项
n个节点构成的不同二叉树数量为卡特兰数的第n项

## 容斥原理

k个集合时这个数会被选中$C_k^k$

$T=C_k^1-C_k^2+C_k^3-...+(-1)^{k+1}C_k^k$

$(1-x)^k=C_k^0-C_k^1x+C_k^2x^2-...+(-1)^kC_k^kx^k$

$(1-x)^k=C_k^0-(C_k^1x-C_k^2x^2+...-(-1)^kC_k^kx^k)$
x取1，发现后面的式子等于T

## 博弈

$SG(p)=SG(G_1) xorSG(G_2)xor...xorSG(G_m)$

upd：辣鸡忘了阶梯博弈

## 数学小姿势

1：一个合数m中一定含有至少一个小于$\sqrt m$的质因子

2：N的所有正约数和可以表示为

$(1+p_1+p_1^2+...+p_1^{c1})*(1+p_2+p_2^2+...+p_2^{c2})*...*(1+p_n+p_n^2+...+p_n^{cn})$

$p_1^{g1}*p_2^{g2}*...*p_n^{gn}$，其中g1<=c1,g2<=c2，以此类推

3：1~N的每个数约数和大约为$N\log n$

4：对于任意a,b<=n
$gcd(a,b)=gcd(a,a-b)$$gcd(2a,2b)=2gcd(a,b)$

$gcd(a,b)=d$，则a,b均可以表示为dk1,dk2，a-b即可以表示为d*(k1-k2)，此情况下一式显然成立

5：对于单求一个数n的欧拉函数有如下公式
$n=p_1^{c1}*p_2^{c2}*...*p_n^{cn}$
$\phi n=n*(p1-1)/p1*(p2-1)/p2*...*(pn-1)/pn$
$=n*(1-1/p1)*(1-1/p2)*...*(1-1/pn)$

6：对于任意正整数n，在1~n中与之互质的数的和为$n*\phi n/2$

7：若a,b互质，则$\phi (ab)=\phi(a)*\phi(b)$

8：费马小定理：若p是质数，则对于任意整数a有：
$a^p\equiv a(\mod p)$$a^{p-1}\equiv 1(\mod p)$

9：欧拉定理：若正整数a,n互质，则有
$a^{\phi n}\equiv 1(\mod n)$

$a^b\equiv a^{b \mod \phi n}$

10：组合数性质
$C_n^m= \frac{n!}{m!*(n-m)}$
$C_n^m=C_n^{n-m}$
n个中取出m个的集合，剩下一定有n-m个的集合与之一一对应
$C_n^m=C_{n-1}^m+C_{n-1}^{m-1}$

$C^0+C^1+C^2+...+C^n=2^n$
n个元素中任取组成集合，有n+1种方法即为取0,1,2,…,n个，分别为$C^0,C^1...C^n$

11：二项式定理
$(a+b)^n=\sum_{k=0}^nC_n^ka^kb^{n-k}$

12：错排

$D[n]$表示n个元素错排的方案数
$D[n]=(n-1)*(D[n-1]+D[n-2])$

## Floyd求最小环

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
struct nd{int pa[105],ln,c;}mp[105][105];
int a[105][105],n,m;
int path[105],len;
void getpa(int i,int j,int k)
{
len=1;path[1]=i;
for(int u=1;u<=mp[i][j].ln;u++)path[++len]=mp[i][j].pa[u];
path[++len]=k;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)mp[i][j].c=2e9;
mem(a,63);
for(int i=1;i<=m;i++)
{
int x,y,c;scanf("%d%d%d",&x,&y,&c);
mp[x][y].c=mp[y][x].c=min(mp[x][y].c,c);
mp[x][y].ln=1;mp[x][y].pa[1]=y;
mp[y][x].ln=1;mp[y][x].pa[1]=x;
a[x][y]=a[y][x]=min(a[x][y],c);
}
int ans=999999999;
for(int k=1;k<=n;k++)
{
for(int i=1;i<k;i++)
for(int j=i+1;j<k;j++)
if(a[j][k]<1e9 && a[k][i]<1e9 && mp[i][j].c<1e9)
if(ans>mp[i][j].c+a[j][k]+a[k][i])
{
ans=mp[i][j].c+a[j][k]+a[k][i];
getpa(i,j,k);
}
for(int i=1;i<=n;i++)if(i!=k)
for(int j=1;j<=n;j++)if(j!=k && j!=i)
if(mp[i][k].c<1e9 && mp[k][j].c<1e9)
if(mp[i][j].c>mp[i][k].c+mp[k][j].c)
{
for(int u=1;u<=mp[i][k].ln;u++)mp[i][j].pa[u]=mp[i][k].pa[u];
for(int u=1;u<=mp[k][j].ln;u++)mp[i][j].pa[u+mp[i][k].ln]=mp[k][j].pa[u];
mp[i][j].ln=mp[i][k].ln+mp[k][j].ln;
mp[i][j].c=mp[i][k].c+mp[k][j].c;
}
}
if(ans==999999999)printf("No solution.\n");
else
{
//	printf("%d\n",ans);
for(int i=1;i<len;i++)printf("%d ",path[i]);
printf("%d\n",path[len]);
}
return 0;
}


## 最小度限制生成树

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<map>
using namespace std;
struct node{int x,y,c,next,other;bool v;}a[21000];int len,last[1100];
void ins(int x,int y,int c){len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].v=true;a[len].next=last[x];last[x]=len;}
struct edge{int x,y,c;bool v;}e[11000];
bool cmp(edge n1,edge n2){return n1.c<n2.c;}
map<string,int> q;int cnt;
char ch[105];
int fa[1100];
int findfa(int x){return fa[x]==x?fa[x]:fa[x]=findfa(fa[x]);}
int n,belong[1100],S;
int maxline,maxcal;
bool fd(int pos,int fa)
{
if(pos==1)return true;
for(int k=last[pos];k;k=a[k].next)
if(a[k].v&&fa!=a[k].y)
{
int y=a[k].y;
if(fd(y,pos))
{
if(maxcal<a[k].c)maxline=k,maxcal=a[k].c;
return true;
}
}
return false;
}
int pushup()
{
int u=-1,gg=0,po=0;
for(int i=1;i<=n;i++)
if(e[i].x==1&&e[i].v)
{
int x=e[i].x,y=e[i].y,cost=e[i].c;
maxcal=0;fd(e[i].y,0);
if(cost>maxcal)continue;
else if(maxcal-cost>po){po=maxcal-cost;gg=maxline,u=i;}
}
if(u==-1)return -1;
//	printf("update:%d %d %d\n",e[u].x,e[u].y,e[u].c);
int x=e[u].x,y=e[u].y,cost=e[u].c;e[u].v=false;
maxline=gg;
a[maxline].v=a[a[maxline].other].v=false;
//	printf("checknow:%d %d %d\n",a[maxline].x,a[maxline].y,a[maxline].c);
ins(e[u].x,e[u].y,e[u].c);ins(e[u].y,e[u].x,e[u].c);
a[len].other=len-1;a[len-1].other=len;
return po;
}
int main()
{
q.clear();
ch[1]='P';ch[2]='a';ch[3]='r';ch[4]='k';q[ch+1]=++cnt;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
if(q[ch+1]==0)q[ch+1]=++cnt;
e[i].x=q[ch+1];
scanf("%s",ch+1);
if(q[ch+1]==0)q[ch+1]=++cnt;
e[i].y=q[ch+1];
if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
scanf("%d",&e[i].c);e[i].v=true;
}
sort(e+1,e+1+n,cmp);
for(int i=1;i<=cnt;i++)fa[i]=i;
int ans=0;
for(int i=1;i<=n;i++)
if(e[i].x!=1&&e[i].y!=1)
{
int u=findfa(e[i].x),v=findfa(e[i].y);
if(u!=v)
{
fa[u]=v;
ins(e[i].x,e[i].y,e[i].c);ins(e[i].y,e[i].x,e[i].c);
a[len].other=len-1;a[len-1].other=len;
ans+=e[i].c;e[i].v=false;
}
}
scanf("%d",&S);
int block=0;
for(int i=2;i<=cnt;i++)
if(fa[i]==i)belong[i]=++block;
for(int i=2;i<=cnt;i++)belong[i]=belong[findfa(i)];
for(int i=1;i<=block;i++)
{
int u=0;
for(int j=1;j<=n;j++)
if(e[j].x==1 && belong[e[j].y]==i){u=j;break;}
ins(e[u].x,e[u].y,e[u].c);
ins(e[u].y,e[u].x,e[u].c);ans+=e[u].c;e[u].v=false;
a[len].other=len-1;a[len-1].other=len;
}
//	for(int i=1;i<=n;i++)printf("CHKER:%d %d %d %d\n",e[i].x,e[i].y,e[i].c,e[i].v);
if(block==S){printf("Total miles driven: %d\n",ans);return 0;}
for(int i=block+1;i<=S;i++)
{
int tmp=pushup();
if(tmp<=0)break;
ans-=tmp;
}
printf("Total miles driven: %d\n",ans);
return 0;
}


## 割点与割边

##无向图点双联通

void tarjan(int x)
{
dfn[x]=low[x]=++id;sta[++tp]=x;
if(x==root && last[x]==0)
{
dcc[++cnt].push_back(x);
return ;
}
int flag=0;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(dfn[y]==-1)
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
flag++;
if(x!=root || flag>1)cut[x]=1;
int i;cnt++;
do
{
i=sta[tp--];
dcc[cnt].push_back(i);
}while(i!=y);
dcc[cnt].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
}


int gg=cnt;
for(int i=1;i<=n;i++)if(cut[i])belong[i]=++gg;
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<dcc[i].size();j++)
{
int x=dcc[i][j];
if(cut[x])
{
ins(belong[x],i),ins(i,belong[x]);
}
cal[x]=i;
}


## 2-sat的输出方案

for(int i=1;i<=n;i++)opp[belong[i]]=belong[i+n],opp[belong[i+n]]=belong[i];
for(int i=1;i<=2*n;i++)val[i]=belong[i]>opp[belong[i]];


## 二分图的一些性质

1：二分图的最小点覆盖=最大匹配

2：二分图的最大独立集=总点数-最小点覆盖=总点数-最大匹配

3：无向图G的最大团=其补图G’的最大独立集

4：给定一张n个点的DAG图G，要求其最小不可重路径覆盖（即用尽量少的不相交路径，覆盖DAG图的每个顶点刚好一次）的做法：将图中每个点i拆点i和i+n。1n的点置于二分图左侧，n+12*n的点置于右侧。原图中的边(x,y)改为(x,y+n)。对这个二分图求最大匹配，那么DAG图G的最小路径覆盖即为n-二分图最大匹配

5：给定一张n个点的DAG图G，要求其最小可重路径覆盖（即每个点可以被覆盖多次）。先对这张图进行传递闭包，即假设(x,y)间接连通我们直接添上一条有向边x->y。然后对这张新图做最小可重路径覆盖即可

6：二分图G的必须边与可行边