模板
输入输出
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
数据结构
线段树
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const ll mod=1000000007;
const ll N=50010;
ll ans[N];
struct node
{
ll l,r,v,lazy;
}node[N<<2]; // 线段树的空间大概是数组空间的4倍;
void build(ll l,ll r,ll numb) // 线段树的建立;
{
node[numb].l=l;
node[numb].r=r;
node[numb].v=0;
node[numb].lazy=0; // 用了lazy思想,提高了效率;
if(l==r) return;
ll mid=(l+r)>>1;
build(l,mid,numb<<1);
build(mid+1,r,numb<<1|1);
}
void PushUp(ll numb) // 往上往父节点方向更新数据;但是这里不是左右儿子的和,而是最大值,因为是站台人数;
{
node[numb].v=((node[numb<<1].v%mod)*(node[numb<<1|1].v%mod))%mod;
}
void PushDown(ll numb) // 向下往左右儿子方向更新数据;
{
node[numb<<1].lazy+=node[numb].lazy;
node[numb<<1|1].lazy+=node[numb].lazy;
node[numb<<1].v+=node[numb].lazy;
node[numb<<1|1].v+=node[numb].lazy;
node[numb].lazy=0; // 更新完了要清零;
}
void Insert(ll l,ll r,ll numb,ll v) // 插入更新数据;
{
if(node[numb].l==l&&node[numb].r==r) // 如果区间完全重合,则不需要再往下更新了,先保存起来,可以节约很多的时间(lazy思想)
{
node[numb].v=v%mod;
node[numb].lazy+=1;
return;
}
if(node[numb].lazy) PushDown(numb); // 因为没有找到完全重合的区间,所以要先更新下一层区间;
ll mid=(node[numb].r+node[numb].l)>>1;
if(l>mid) Insert(l,r,numb<<1|1,v);
else if(r<=mid) Insert(l,r,numb<<1,v);
else{
Insert(l,mid,numb<<1,v);
Insert(mid+1,r,numb<<1|1,v);
}
PushUp(numb); // 最后还得往上返回,更新父节点区间;
}
ll query(ll l,ll r,ll numb) // 查询区间l到r;
{
if(node[numb].l==l&&node[numb].r==r){
return node[numb].v%mod;
}
//if(node[numb].lazy) PushDown(numb); // 道理同48行;
ll mid=(node[numb].r+node[numb].l)>>1;
if(l>mid) return query(l,r,numb<<1|1)%mod;
else if(r<=mid) return query(l,r,numb<<1)%mod;
else{
return ((query(l,mid,numb<<1)%mod)*(query(mid+1,r,numb<<1|1)%mod)%mod); // 道理同28行;
}
}
ll t,n,k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
build(1,50000,1);
cin>>n;
for(int i=1;i<=n;++i){
cin>>ans[i];
Insert(i,i,1,ans[i]);
}
cin>>k;
for(int i=0;i<k;++i){
int c,l,r;
cin>>c;
if(c==0){
cin>>l>>r;
cout<<query(l,r,1)<<"\n";
}
if(c==1){
cin>>l>>r;
Insert(l,l,1,r);
}
}
}
return 0;
}
主席树
- 非结构体
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int N=100000;
int a[N+10],b[N+10];
int t,m,n,q,p,tot;
int ls[N*30],rs[N*30],sum[N*30],root[N*30];
void build(int &now,int l,int r)
{
now=++tot,sum[now]=0;
if(l==r) return;
int mid=(l+r)>>1;
build(ls[now],l,mid);
build(rs[now],mid+1,r);
}
int update(int lastRoot,int l,int r)
{
int newRoot=++tot;
ls[newRoot]=ls[lastRoot],rs[newRoot]=rs[lastRoot],sum[newRoot]=sum[lastRoot]+1;
if(l==r) return newRoot;
int mid=(r+l)>>1;
if(mid>=p) ls[newRoot]=update(ls[newRoot],l,mid);
else rs[newRoot]=update(rs[newRoot],mid+1,r);
return newRoot;
}
int query(int u,int v,int l,int r,int k)
{
int mid=(r+l)>>1;
int x=sum[ls[v]]-sum[ls[u]];
if(l==r) return l;
if(x>=k) return query(ls[u],ls[v],l,mid,k);
else return query(rs[u],rs[v],mid+1,r,k-x);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
tot=0;
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+1+n);
q=unique(b+1,b+1+n)-b-1;
build(root[0],1,q);
for(int i=1;i<=n;++i){
p=lower_bound(b+1,b+1+q,a[i])-b;
root[i]=update(root[i-1],1,q);
}
for(int i=0;i<m;++i){
int l,r,k;
cin>>l>>r>>k;
cout<<b[query(root[l-1],root[r],1,q,k)]<<"\n";
}
}
return 0;
}
- 结构体
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int N=100000;
int t,n,m,q,mid;
ll x;
int a[N+10],b[N+10],root[N*30];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
struct treeNode{
ll sum;
int ls,rs;
};
struct chairmanTree{
int tot;//记录最新的根节点索引
treeNode tree[N*30];
void build(int &now,int l,int r)
{
now=++tot;
tree[now].sum=0;
if(l==r) return;
int mid=(l+r)>>1;
build(tree[now].ls,l,mid);
build(tree[now].rs,mid+1,r);
}
int update(int lastRoot,int l,int r,int p)//lastNode->前一个根节点 p->更新的数字在数组中Kth
{
int newRoot=++tot;
tree[newRoot].ls=tree[lastRoot].ls;//连接左子树
tree[newRoot].rs=tree[lastRoot].rs;//连接右子树
tree[newRoot].sum=tree[lastRoot].sum+1;//更新sum
if(l==r){
return newRoot;
}
int mid=(r+l)>>1;
if(mid >= p){
tree[newRoot].ls=update(tree[newRoot].ls,l,mid,p);//更新左子树,新建一个左子树
}else{
tree[newRoot].rs=update(tree[newRoot].rs,mid+1,r,p);
}
return newRoot;
}
int query(int u,int v,int l,int r,int k)//u、v为两棵线段树当前节点编号,相减就是询问区间
{
mid=(l+r)>>1;
x=tree[tree[v].ls].sum-tree[tree[u].ls].sum;
if(l==r)
return r;//或者return l也可以,但是l和1太像了
if(x>=k)
return query(tree[u].ls,tree[v].ls,l,mid,k);
else
return query(tree[u].rs,tree[v].rs,mid+1,r,k-x);
}
}T;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
//t=read();
//while(t--){
int p=0;
T.tot=0,q=0;
memset(root,0,sizeof(root));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
//cin>>n>>m;
n=read(),m=read();
for(int i=1;i<=n;++i){
//cin>>a[i];
a[i]=read();
b[i]=a[i];
}
sort(b+1,b+1+n);
q=unique(b+1,b+1+n)-b-1;
T.build(root[0],1,q);
for(int i=1;i<=n;++i){
p=lower_bound(b+1,b+1+q,a[i])-b;
root[i]=T.update(root[i-1],1,q,p);
}
while(m--){
int l,r,k;
//cin>>l>>r>>k;
l=read(),r=read(),k=read();
//cout<<b[T.query(root[l-1],root[r],1,q,k)]<<"\n";
printf("%d\n",b[T.query(root[l-1],root[r],1,q,k)]);
}
//}
return 0;
}
树状数组
图论
最短路
dijkstra
#include<bits/stdc++.h>
using namespace std;
const int INF = 1e6;
const int NUM = 105;
struct edge{
int from, to, w;
edge(int a, int b, int c){from=a, to=b, w=c;}
};
vector<edge>e[MUN];
struct s_node{
int id,n_dis;
s_node(int b, int c){id = b; n_dis = c;}
bool operator < (const s_node & a) const {
return n_dis > a.n_dis;
}
};
int n,m;
int pre[NUM];
void print_path(int s,int t){
if(s==t){
printf("%d ",s);
return;
}
print_path(s,pre[t]);
printf("%d ",t);
}
void dijkstra()
{
int s=1;
int dis[NUM];
bool done[NUM];
for(int i=1;i<=n;++i){
dis[i]=INF;
done[i]=false;
}
dis[s]=0;
priority_queue<s_node>Q;
Q.push(s_node(s,dis[s]));
while(!Q.empty()){
s_node u=Q.top();
Q.pop();
if(done[u.id])
continue;
done[u.id]=true;
for(int i=0;i<e[u.id].size();++i){
edge y=e[u.id][i];
if(done[y.to])
continue;
if(dis[y.to]>y.w+u.n_dis){
dis[y.to]=y.w+u.n_dis;
Q.push(s_node(y.to,dis[y.to]));
pre[y.to]=u.id;
}
}
}
printf("%d\n",dis[n]);
//print_path(s,n);
}
int main()
{
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) return 0;
for(int i=1;i<=n;++i){
e[i].clear();
}
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
e[a].push_back(edge(a,b,c));
e[b].push_back(edge(b,a,c));
}
dijkstra();
}
}
spfa
#include<bits/stdc++.h>
using namespace std;
const int INF=INT32_MAX/10;
const int NUM=1000005;
struct Edge{
int to,next,w;
}edge[NUM];
int n,m,cnt;
int head[NUM];
int dis[NUM];
bool inq[NUM];
int Neg[NUM];
int pre[NUM];
void print_paht(int s,int t){
;
}
void init()
{
for(int i=0;i<NUM;++i){
edge[i].next=-1;
head[i]=-1;
}
cnt=0;
}
void addedge(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
int spfa(int s)
{
memset(Neg,0,sizeof(Neg));
Neg[s]=1;
for(int i=1;i<=n;++i){
dis[i]=INF;
inq[i]=false;
}
dis[s]=0;
queue<int>Q;
Q.push(s);
inq[s]=true;
while(!Q.empty()){
int u=Q.front();
Q.pop();
inq[u]=false;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to, w=edge[i].w;
if(dis[u]+w<dis[v]){
dis[v]=dis[u]+w;
pre[v]=u ;
if(!inq[v]){
inq[v]=true;
Q.push(v);
Neg[v]++;
if(Neg[v]>n) return 1;
}
}
}
}
printf("%d\n",dis[n]);
//print_path(s,n);
return 0;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
init();
if(n==0&&m==0) return 0;
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
spfa(1);
}
return 0;
}
floye-warshall
#include<bits/stdc++.h>
using namespace std;
const int INF=1e6;
const int NUM=105;
int graph[NUM][NUM];
int n,m;
void floyd()
{
int s=1;
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
if(graph[i][k]!=INF){
for(int j=1;j<=n;j++){
if(graph[i][j]>graph[i][k]+graph[k][j])
graph[i][j]=graph[i][k]+graph[k][j];
}
}
}
}
printf("%d\n",graph[s][n]);
}
int main()
{
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) return 0;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j)
graph[i][j]=INF;
}
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
graph[a][b]=graph[b][c]=c;
}
floyd();
}
return 0;
}
LCA
倍增
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+7;
vector<int> v[maxn], w[maxn];
int fa[maxn][31], cost[maxn][31], dep[maxn];
int n, m;
int a, b, c;
void dfs(int root, int fno)//fno->父节点
{
fa[root][0] = fno;
dep[root] = dep[fa[root][0]] + 1;
for (int i = 1; i < 31; ++i) {
fa[root][i] = fa[fa[root][i-1]][i-1];
cost[root][i] = cost[fa[root][i-1]][i-1] + cost[root][i-1];
}
int sz = v[root].size();
for (int i = 0; i < sz; ++i) {
if (v[root][i] == fno) continue;
cost[v[root][i]][0] = w[root][i]; //初始化每个结点的cost
dfs(v[root][i], root);
}
}
int lca(int x, int y)
{
if(dep[x] > dep[y]) swap(x, y);
int tmp = dep[y] - dep[x], ans = 0;
for (int j =0; tmp; ++j, tmp >>= 1)
if (tmp & 1) ans +=cost[y][j], y = fa[y][j];
if(y == x) return ans;
for (int j = 30; j >=0 && y != x; --j) {
if(fa[x][j] != fa[y][j]) {
ans += cost[x][j] + cost[y][j];
x = fa[x][j];
y = fa[y][j];
}
}
ans += cost[x][0] + cost[y][0];
return ans;
}
int main()
{
memset(fa, 0, sizeof(fa));
memset(cost, 0, sizeof(cost));
memset(dep, 0, sizeof(dep));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &a, &b, &c);
// ++a, ++b;
v[a].push_back(b);
v[b].push_back(a);
w[a].push_back(c);
w[b].push_back(c);
}
dfs(1,0);
scanf("%d", &m);
for (int i = 0; i < m; ++i) {
scanf("%d%d", &a, &b);
// ++a, ++b;
printf("%d\n", lca(a, b));
}
return 0;
}
树分治
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 10;
const int inf = 2e9;
int n, m, a, b, c, rt;
int q[maxn], siz[maxn], maxx[maxn], dist[maxn];
int cur, h[maxn], nxt[maxn], p[maxn], w[maxn];
bool tf[10000010], ret[maxn], vis[maxn];
void add_edge(int x, int y, int z)
{
cur++; //cur -> tot
nxt[cur] = h[x];
h[x] = cur;
p[cur] = y;
w[cur] = z;
}
int sum;
void calcsiz(int x, int fa)// 找重心
{
siz[x] = 1;
maxx[x] = 0;
for (int j = h[x]; j; j = nxt[j]) {
if (p[j] != fa && !vis[p[j]]) {
calcsiz(p[j], x);
maxx[x] = max(maxx[x], siz[p[j]]);
siz[x] += siz[p[j]];
}
}
maxx[x] = max(maxx[x], sum - siz[x]);
if(maxx[x] < maxx[rt]) rt = x;
}
queue<int> tag;
int dd[maxn], cnt;
void calcdist(int x, int fa)
{
dd[++cnt] = dist[x];
for (int j = h[x]; j; j = nxt[j])
if (p[j] != fa && !vis[p[j]])
dist[p[j]] = dist[x] + w[j], calcdist(p[j], x);
}
void dfz(int x, int fa)
{
tf[0] = true;
tag.push(0);
vis[x] = true;
for (int j = h[x]; j; j = nxt[j]) {
if (p[j] != fa && !vis[p[j]]) {
dist[p[j]] = w[j];
calcdist(p[j], x);
for (int k = 1; k <= cnt; ++k) {
for (int i = 1; i <= m ; ++i){
if( q[i] >= dd[k])
ret[i] |= tf[q[i] - dd[k]];
}
}
for (int k = 1; k <= cnt; ++k)
if (dd[k] < 10000010)
tag.push(dd[k]), tf[dd[k]] = true;
cnt = 0;
}
}
while (!tag.empty())
tf[tag.front()] = false, tag.pop();
for (int j = h[x]; j; j = nxt[j]) {
if (p[j] !=fa && !vis[p[j]]) {
sum = siz[p[j]];
rt = 0;
maxx[rt] = inf;
calcsiz(p[j], x);
calcsiz(rt, -1);
dfz(rt, x);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i < n; ++i)
scanf("%d%d%d", &a, &b, &c), add_edge(a, b, c), add_edge(b, a, c);
for (int i = 1; i <= m; ++i)
scanf("%d", q + 1);
rt = 0;
maxx[rt] = inf;
sum = n;
calcsiz(1, -1);// 寻找重心
calcsiz(rt, -1);
dfz(rt, -1);
}
树的重心
// 这份代码默认节点编号从 1 开始,即 i ∈ [1,n]
int size[MAXN], // 这个节点的“大小”(所有子树上节点数 + 该节点)
weight[MAXN], // 这个节点的“重量”
centroid[2]; // 用于记录树的重心(存的是节点编号)
void GetCentroid(int cur, int fa) { // cur 表示当前节点 (current)
size[cur] = 1;
weight[cur] = 0;
for (int i = head[cur]; i != -1; i = e[i].nxt) {
if (e[i].to != fa) { // e[i].to 表示这条有向边所通向的节点。
GetCentroid(e[i].to, cur);
size[cur] += size[e[i].to];
weight[cur] = max(weight[cur], size[e[i].to]);
}
}
weight[cur] = max(weight[cur], n - size[cur]);
if (weight[cur] <= n / 2) { // 依照树的重心的定义统计
centroid[centroid[0] != 0] = cur;
}
}
字符串
KMP
-
最初kmp模板
-
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+5; char s[maxn],p[maxn]; int Next[maxn]; int cnt; void getFail() { Next[0]=0,Next[1]=0; for(int i=1;i<strlen(p)-1;++i){ int j=Next[i]; while(j && p[i]!=p[j]) j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } void kmp() { int m=strlen(p); int last=-1; int slen=strlen(s),plen=strlen(p); getFail(); int j=0; for(int i=0;i<slen;++i){ while(j && s[i]!=p[j]) j=Next[j]; if(s[i]==p[j]) j++; if(j==m) printf("%d",i-m+1); } } int main() { ios::sync_with_stdio(false); cin>>s>>p; kmp(); return 0; }
-
next数组获取函数
-
void GetNext(char *p, int *next)// 模式串 { int plen = strlen(p) ; next[0] = -1 ; int k = -1 ; int j = 0 ; while(j< plen-1) { // p[k]表示前缀 , p[j]表示后缀 ; if(k==-1 || p[j] == p[k]) { k++; j++; next[j] = k ; } else { k = next[k] ; } } }
-
优化next函数
-
void GetNextval(char* p, int next[]) { int pLen = strlen(p); next[0] = -1; int k = -1; int j = 0; while (j < pLen ) { //p[k]表示前缀,p[j]表示后缀 if (k == -1 || p[j] == p[k]) { j++; k++; //较之前next数组求法,改动在下面4行 if (p[j] != p[k]) next[j] = k; //之前只有这一行 else //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]] next[j] = next[k]; } else { k = next[k]; } } }
-
exkmp
-
#include<bits/stdc++.h> using namespace std; char p[10005],s[1000005]; int next[100005],extend[1000005]; void getNext(char *p, int &plen, int *nexts) { int a = 0,k = 0; nexts[0] = plen; for (int i = 1;i < plen; i++) { if (i >= k || i + nexts[i-1] >= k) { if (i >= k) k = i; while (k <plen && p[k] == p[k-i]) k++; nexts[i] = k - i; a = i; } else nexts[i] = nexts[i - a]; } } void extendkmp (char *s, int &slen, char *p, int &plen, int *extend, int *next) { int a = 0, k = 0; getNext (p, plen, next); for (int i = 0; i < slen; i++) { if (i >=k || i +enxts[i - a] >= k) { if (i >= k) k = i; while (k <slen && k - i < plen && s[k] == p[k -i]) k ++; exten[i] = k - i; a = i; } else extend[i] = nexts[i - a]; } } int main() { int slen, plen; while (cin >> s >>p) { slen = strlen(s); plen = strlen(p); extendkmp(s, slen, p, plen, extend, nexts); cou << "next: "; for (int i = 0; i < plen; ++i) cout << nexts[i] << " "; cout <<"\nextend: "; for (int i = 0; i < slen; ++i) cout << extend[i] << " "; cout <<"\n\n"; } return 0; }
字符串hash
-
自然溢出hash
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
ull base=131;
ull a[10010];
char s[10010];
int n,ans=1;
ull hashs(char s[])
{
int len=strlen(s);
ull ans=0;
for (int i=0;i<len;i++)
ans=ans*base+(ull)s[i];
return ans&0x7fffffff;
}
main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s);
a[i]=hashs(s);
}
sort(a+1,a+n+1);
for (int i=2;i<=n;i++)
if (a[i]!=a[i-1])
ans++;
printf("%d\n",ans);
}
- 最稳妥的办法是选择两个10^9级别的质数,只有模这两个数都相等才判断相等,但常数略大,代码相对难写,目前暂时没有办法卡掉这种写法(除了卡时间让它超时)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
ull base=131;
struct data
{
ull x,y;
}a[10010];
char s[10010];
int n,ans=1;
ull mod1=19260817;
ull mod2=19660813;
ull hash1(char s[])
{
int len=strlen(s);
ull ans=0;
for (int i=0;i<len;i++)
ans=(ans*base+(ull)s[i])%mod1;
return ans;
}
ull hash2(char s[])
{
int len=strlen(s);
ull ans=0;
for (int i=0;i<len;i++)
ans=(ans*base+(ull)s[i])%mod2;
return ans;
}
bool comp(data a,data b)
{
return a.x<b.x;
}
main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s);
a[i].x=hash1(s);
a[i].y=hash2(s);
}
sort(a+1,a+n+1,comp);
for (int i=2;i<=n;i++)
if (a[i].x!=a[i-1].x || a[i-1].y!=a[i].y)
ans++;
printf("%d\n",ans);
}
-
如果能背过或在考场上找出一个10^18级别的质数(Miller-Rabin),也相对靠谱,主要用于前一种担心会超时
这是只用一个10^18质数的hash(100)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef unsigned long long ull; ull base=131; ull a[10010]; char s[10010]; int n,ans=1; ull mod=212370440130137957ll;//是质数!! ull hashs(char s[]) { int len=strlen(s); ull ans=0; for (int i=0;i<len;i++) ans=(ans*base+(ull)s[i])%mod; return ans; } main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%s",s); a[i]=hashs(s); } sort(a+1,a+n+1); for (int i=2;i<=n;i++) if (a[i]!=a[i-1]) ans++; printf("%d\n",ans); }
二分
while(l<=r)
{
int mid=(l+r)/2;//min=l+(r-l)/2 防止数据溢出
if(check(mid))
l=mid+1;
else
r=mid-1;
}
//最大值最小答案是l,最小值最大答案是r
下面扒了别人的一篇博客
最大化最小值(二分值最小越小越容易满足条件,求最大值)
区间长度为1时的写法:
解的范围为
// 计算区间为[lb, rb]
while( rb > lb ) // 区间长度为1时终止循环
{
// 防止溢出
int m = lb + (rb - lb + 1) / 2; // 由于是区间长度为1时终止循环,所以要加1
if( ok(m) ) lb = m;
else rb = m - 1;
}
// 跳出循环时 lb == rb
区间长度为2时的写法:
解的范围为
while( rb - lb > 1 ) // 区间长度为2时终止循环
{
// 防止溢出
int m = lb + (rb - lb) / 2; // 由于是区间长度为2时终止循环,所以不用加1(不会死循环)
if( ok(m) ) lb = m;
else rb = m;
}
// 跳出循环时 lb + 1 == rb
// 答案为 lb
最小化最大值(二分值越大越容易满足条件,求最小值)
区间长度为1时的写法:
解的范围为
while( rb > lb )
{
// 防止溢出
int m = lb + (rb - lb) / 2; // 这里虽然区间长度为1,但不需要加1(不会死循环)
if( ok(m) ) rb = m;
else lb = m + 1;
}
// 跳出循环时 lb == rb
区间长度为2时的写法:
解的范围为
while( rb - lb > 1 )
{
// 防止溢出
int m = lb + (rb - lb) / 2;
if( ok(m) ) rb = m;
else lb = m;
}
// 跳出循环时 lb + 1 == rb
// 答案为 rb
浮点数的二分,100次循环可以达到2-100(约为10-30)的精度范围
以最大化最小值为例(即小于该数的解均满足条件)
for( int i = 0; i < 100; ++ i )
{
double m = (lb + rb) / 2;
if(check(m)) lb=m;
else rb=m;
}
// 跳出循环时 lb 与 rb 近似相等,所以都可作为答案
数学
数论
费马小定理
LL ksm(LL x,LL y){
LL ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
lucas
LL C(LL n,LL m){
if(m>n)return 0;
LL s1=1,s2=1;
for(LL i=n-m+1;i<=n;i++)s1=s1*i%mod;
for(LL i=1;i<=m;i++)s2=s2*i%mod;
return s1*ksm(s2,mod-2);
}
LL Lucas(LL n,LL m){
if(!m)return 1;
return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}
Pallard's Rho 大数质因数分解
#include<iostream>
#include<ctime>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
map<ll, int>m;
const int mod = 10000019;
const int times = 50;//测试50次
ll mul(ll a, ll b, ll m)
//求a*b%m
{
ll ans = 0;
a %= m;
while(b)
{
if(b & 1)ans = (ans + a) % m;
b /= 2;
a = (a + a) % m;
}
return ans;
}
ll pow(ll a, ll b, ll m)
//a^b % m
{
ll ans = 1;
a %= m;
while(b)
{
if(b & 1)ans = mul(a, ans, m);
b /= 2;
a = mul(a, a, m);
}
ans %= m;
return ans;
}
bool Miller_Rabin(ll n, int repeat)//n是测试的大数,repeat是测试重复次数
{
if(n == 2 || n == 3)return true;//特判
if(n % 2 == 0 || n == 1)return false;//偶数和1
//将n-1分解成2^s*d
ll d = n - 1;
int s = 0;
while(!(d & 1)) ++s, d >>= 1;
//srand((unsigned)time(NULL));在最开始调用即可
for(int i = 0; i < repeat; i++)//重复repeat次
{
ll a = rand() % (n - 3) + 2;//取一个随机数,[2,n-1)
ll x = pow(a, d, n);
ll y = 0;
for(int j = 0; j < s; j++)
{
y = mul(x, x, n);
if(y == 1 && x != 1 && x != (n - 1))return false;
x = y;
}
if(y != 1)return false;//费马小定理
}
return true;
}
ll gcd(ll a, ll b)
{
return b == 0 ? a : gcd(b, a % b);
}
ll pollard_rho(ll n, ll c)//找到n的一个因子
{
ll x = rand() % (n - 2) + 1;
ll y = x, i = 1, k = 2;
while(1)
{
i++;
x = (mul(x, x, n) + c) + n;//不断调整x2
ll d = gcd(y - x, n);
if(1 < d && d < n)
return d;//找到因子
if(y == x)
return n;//找到循环,返回n,重新来
if(i == k)//一个优化
{
y = x;
k <<= 1;
}
}
}
void Find(ll n, ll c)
{
if(n == 1)return;//递归出口
if(Miller_Rabin(n, times))//如果是素数,就加入
{
m[n]++;
return;
}
ll p = n;
while(p >= n)
p = pollard_rho(p, c--);//不断找因子,知道找到为止,返回n说明没找到
Find(p, c);
Find(n / p, c);
}
int main()
{
ll n;srand((unsigned)time(NULL));
while(cin >> n)
{
m.clear();
Find(n, rand() % (n - 1) + 1);//这是自己设置的一个数
cout<<n<<" = ";
for(map<ll ,int>::iterator it = m.begin(); it != m.end();)
{
cout<<it->first<<" ^ "<<it->second;
if((++it) != m.end())
cout<<" * ";
}
cout<<endl;
}
return 0;
}
欧拉筛
int pre[1000000+10];
int vis[1000000+10];
vector<int>isp;
void eur()
{
for(auto i=2;i<1e6+10;++i){
if(!vis[i]){
isp.push_back(i);
pre[i]=i;
}
for(auto j:isp){
if(i*j>=1e6+10) break;
vis[i*j]=1;
pre[i*j]=j;
if(i%j==0) break;
}
}
}
void init() {
phi[1] = 1;
for (int i = 2; i < MAXN; ++i) {
if (!vis[i]) {
phi[i] = i - 1;
pri[cnt++] = i;
}
for (int j = 0; j < cnt; ++j) {
if (1ll * i * pri[j] >= MAXN) break;
vis[i * pri[j]] = 1;
if (i % pri[j]) {
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
} else {
// i % pri[j] == 0
// 换言之,i 之前被 pri[j] 筛过了
// 由于 pri 里面质数是从小到大的,所以 i 乘上其他的质数的结果一定也是
// pri[j] 的倍数 它们都被筛过了,就不需要再筛了,所以这里直接 break
// 掉就好了
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
}
线性代数
高斯消元
- 整型
int a[N][N];//增广矩阵
int x[N];//解集
bool freeX[N];//标记是否为自由变元
int GCD(int a,int b){
return !b?a:GCD(b,a%b);
}
int LCM(int a,int b){
return a/GCD(a,b)*b;
}
int Gauss(int equ,int var){//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=true;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){//col列第row行以下全是0,处理当前行的下一列
row--;
continue;
}
for(int i=row+1;i<equ;i++){//枚举要删去的行
if(a[i][col]!=0){
int lcm=LCM(abs(a[i][col]),abs(a[row][col]));
int ta=lcm/abs(a[i][col]);
int tb=lcm/abs(a[row][col]);
if(a[i][col]*a[row][col]<0)//异号情况相加
tb=-tb;
for(int j=col;j<var+1;j++) {
a[i][j]=a[i][j]*ta-a[row][j]*tb;
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if (a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){//计算解集
int temp=a[i][var];
for(int j=i+1;j<var;j++){
if(a[i][j]!=0)
temp-=a[i][j]*x[j];
}
if(temp%a[i][i]!=0)//有浮点数解,无整数解
return -2;
x[i]=temp/a[i][i];
}
return 0;
}
int main(){
int equ,var;//equ个方程,var个变元
while(scanf("%d%d",&equ,&var)!=EOF) {
memset(a,0,sizeof(a));
for(int i=0;i<equ;i++)//输入增广矩阵
for(int j=0;j<var+1;j++)
scanf("%d",&a[i][j]);
int freeNum=Gauss(equ,var);//自由元个数
if(freeNum==-1)//无解
printf("无解\n");
else if(freeNum==-2)//有浮点数解无整数解
printf("无整数解\n");
else if(freeNum>0){//有无穷多解
printf("有无穷多解,自由变元个数为%d\n",freeNum);
for(int i=0;i<var;i++){
if(freeX[i])
printf("x%d是自由变元\n",i+1);
else
printf("x%d=%d\n",i+1,x[i]);
}
}
else{//有唯一解
for(int i=0;i<var;i++)
printf("x%d=%d\n",i+1,x[i]);
}
printf("\n");
}
return 0;
}
- 浮点
double a[N][N];//增广矩阵
double x[N];//解集
bool freeX[N];//标记是否为自由变元
int Gauss(int equ,int var){//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=true;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(fabs(a[row][col])<1e6){//col列第row行以下全是0,处理当前行的下一列
row--;
continue;
}
for(int i=row+1;i<equ;i++){//枚举要删去的行
if(fabs(a[i][col])>1e6){
double temp=a[i][col]/a[row][col];
for(int j=col;j<var+1;j++)
a[i][j]-=a[row][j]*temp;
a[i][col]=0;
}
}
}
/*求解*/
//无解
for(int i=row;i<equ;i++)
if(fabs(a[i][col])>1e6)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){//计算解集
double temp=a[i][var];
for(int j=i+1;j<var;j++)
temp-=a[i][j]*x[j];
x[i]=temp/a[i][i];
}
return 0;
}
- 模线性
int a[N][N];//增广矩阵
int x[N];//解集
bool freeX[N];//标记是否为自由变元
int GCD(int a,int b){
return !b?a:GCD(b,a%b);
}
int LCM(int a,int b){
return a/GCD(a,b)*b;
}
int Gauss(int equ,int var){//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=true;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){//col列第row行以下全是0,处理当前行的下一列
row--;
continue;
}
for(int i=row+1;i<equ;i++){//枚举要删去的行
if(a[i][col]!=0){
int lcm=LCM(abs(a[i][col]),abs(a[row][col]));
int ta=lcm/abs(a[i][col]);
int tb=lcm/abs(a[row][col]);
if(a[i][col]*a[row][col]<0)//异号情况相加
tb=-tb;
for(int j=col;j<var+1;j++) {
a[i][j]=((a[i][j]*ta-a[row][j]*tb)%MOD+MOD)%MOD;
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if (a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){//计算解集
int temp=a[i][var];
for(int j=i+1;j<var;j++){
if(a[i][j]!=0)
temp-=a[i][j]*x[j];
temp=(temp%MOD+MOD)%MOD;//取模
}
while(temp%a[i][i]!=0)//外层每次循环都是求a[i][i],它是每个方程中唯一一个未知的变量
temp+=MOD;//a[i][i]必须为整数,加上周期MOD
x[i]=(temp/a[i][i])%MOD;//取模
}
return 0;
}
-
异或方程组
-
\[\]\[ \]
int a[N][N];//增广矩阵
int x[N];//解集
int freeX[N];//自由变元
int Gauss(int equ,int var){//返回自由变元个数
/初始化/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=0;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int num=0;//自由变元的序号
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){//col列第row行以下全是0,处理当前行的下一列
freeX[num++]=col;//记录自由变元
row--;
continue;
}
for(int i=row+1;i<equ;i++){
if(a[i][col]!=0){
for(int j=col;j<var+1;j++){//对于下面出现该列中有1的行,需要把1消掉
a[i][j]^=a[row][j];
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if(a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){//计算解集
x[i]=a[i][var];
for(int j=i+1;j<var;j++)
x[i]^=(a[i][j]&&x[j]);
}
return 0;
}
int enumFreeX(int freeNum,int var){//枚举自由元,统计有解情况下1最少的个数
int sta=(1<<(freeNum));//自由元的状态总数
int res=INF;
for(int i=0;i<sta;++i){//枚举状态
int cnt=0;
for(int j=0;j<freeNum;j++){//枚举自由元
if(i&(1<<j)){
cnt++;
x[freeX[j]]=1;
}else
x[freeX[j]]=0;
}
for(int k=var-freeNum-1;k>=0;k--){//没有自由元的最下面一行
int index=0;
for(index=k;k<var;index++){//在当前行找到第一个非0自由元
if(a[k][index])
break;
}
x[index]=a[k][var];
for(int j=index+1;j<var;++j){//向后依次计算出结果
if(a[k][j])
x[index]^=x[j];
}
cnt+=x[index];//若结果为1,则进行统计
}
res=min(res,cnt);
}
return res;
}
int main(){
memset(a,0,sizeof(a));
int equ,var;
scanf("%d%d",&equ,&var);
for(int i=0;i<equ;i++){//构造初始状态
}
for(int i=0;i<equ;i++)//最终状态
scanf("%d",&a[i][var]);
int freeNum=Gauss(equ,var);//获取自由元
if(freeNum==-1)//无解
printf("inf\n");
else if(freeNum==0){//唯一解
int res=0;
for(int i=0;i<var;i++)
res+=x[i];
printf("%d\n",res);
}
else{//多个解
int res=enumFreeX(freeNum,var);
printf("%d\n",res);
}
return 0;
}