zoukankan      html  css  js  c++  java
  • 模板

    输入输出

    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)<<"
    ";
                }
                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)]<<"
    ";
            }
        }
        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)]<<"
    ";
                printf("%d
    ",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
    ",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
    ",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
    ",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
    ", 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 <<"
      extend: ";
      		for (int i = 0; i < slen; ++i)
      			 cout << extend[i] << " ";
      			cout <<"
      
      ";
      	}
      	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
    ",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
    ",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
      ",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("无解
    ");
            else if(freeNum==-2)//有浮点数解无整数解
                printf("无整数解
    ");
            else if(freeNum>0){//有无穷多解
                printf("有无穷多解,自由变元个数为%d
    ",freeNum);
                for(int i=0;i<var;i++){
                    if(freeX[i])
                        printf("x%d是自由变元
    ",i+1);
                    else
                        printf("x%d=%d
    ",i+1,x[i]);
                }
            }
            else{//有唯一解
                for(int i=0;i<var;i++)
                    printf("x%d=%d
    ",i+1,x[i]);
            }
            printf("
    ");
        }
        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
    ");
    else if(freeNum==0){//唯一解
        int res=0;
        for(int i=0;i<var;i++)
            res+=x[i];
        printf("%d
    ",res);
    }
    else{//多个解
        int res=enumFreeX(freeNum,var);
        printf("%d
    ",res);
    }
    return 0;
    

    }

  • 相关阅读:
    POJ 3904 Sky Code (容斥原理)
    HDU 4334 Trouble (暴力)
    UVA 10325 The Lottery( 容斥原理)
    HDU 2841 Visible Trees 数论+容斥原理
    UVA11806-Cheerleaders(容斥原理+二进制)
    HDU1695 GCD (欧拉函数+容斥原理)
    HDU 5651 xiaoxin juju needs help (组合数)
    最大子矩阵和 51Nod 1051 模板题
    最大子段和 模板题 51Nod 1049
    51Nod 1006 最长公共子序列Lcs问题 模板题
  • 原文地址:https://www.cnblogs.com/DrumWashingMachine-Lhy-NoobInCsu/p/14176246.html
Copyright © 2011-2022 走看看