zoukankan      html  css  js  c++  java
  • ACM模板

    优化

    #pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
    #pragma GCC optimize(3 , "Ofast" , "inline")
    #pragma GCC optimize("Ofast")
    #pragma GCC target("avx,avx2,fma")
    #pragma GCC optimization("unroll-loops")
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    

    最小环路径

    ll n , m , g[110][110] , dis[110][110] , z[110][110] ;
    ll ans = INF ;
    vector<int> v ;
    void floyd()
    {
      for(int k = 1 ;k <= n ;k ++){
         for(int i = 1 ;i < k ;i ++ ){
            for(int j = i + 1; j < k ;j ++){
               ll temp = g[i][k] + g[k][j] + dis[i][j] ;
               if(temp < ans){
                  ans = temp ,v.clear() ;
                  int p = j ;
                  while(p != i){
                     v.push_back(p) ;
                     p = z[i][p] ;
                   }
                   v.push_back(i),v.push_back(k) ;
                }
             }
          }
          for(int i = 1; i <= n ;i ++)
           for(int j = 1; j <= n ;j ++){
              ll temp = dis[i][k] + dis[k][j] ;
              if(dis[i][j] > temp)
               dis[i][j] = temp , z[i][j] = z[k][j] ;
            }
       }
    }
    int main()
    {
      n = in() , m = in() ;
      for(int i = 0 ;i < 102 ;i ++)
       for(int j = 0 ; j < 102 ;j ++)
        g[i][j] = INF , dis[i][j] = INF , z[i][j] = i ;
      for(int i = 1; i <= m ;i ++)
       {
         ll u = in() , v = in() , w = in() ;
         g[u][v] = g[v][u] = dis[u][v] = dis[v][u] = min(dis[u][v] , w) ;
       }
      floyd() ;
      if(ans == INF) puts("No solution.") ;
      else
       {
         for(auto x : v)
          cout << x << " " ;
       }
      return 0 ;
    }
    

    二分匹配

    匈牙利匹配
    int find(int u){
      for(auto x : v[u]){
         if(!st[x]){
            st[x] = 1 ;
            if(match[x] == 0 || find(match[x])){
               match[x] = u ;
               return true ;
             }
          }
       }
       return false ;
    }
    int main()
    {
      cin >> n >> m ;
      int a , b ;
      while(cin >> a >> b)
       v[a].push_back(b) ;
      int res = 0 ;
      for(int i = 1 ;i <= n - m ;i ++){
         memset(st , 0 , sizeof st) ;
         if(find(i))
          res ++ ;
       }
      cout << res << endl ;
    }
    
    HK二分匹配
    int dx[N] , dy[N] , n , m ;
    vector<int> v[N] ;
    int vis[N] , match[N] , usedx[N] , usedy[N] , depth;
    bool find(int u) {
      for(auto x : v[u]) {
        if(!vis[x] && dx[u] == dy[x] - 1) {
          vis[x] = 1 ;
          if(!usedy[x] || find(usedy[x])) {
            usedx[u] = x ;
            usedy[x] = u ;
            return true ;
          }
        }
      }
      return false ;
    }
    bool bfs() {
      queue<int> q ;
      depth = INF ;
      memset(dx , -1 , sizeof dx) ;
      memset(dy , -1 , sizeof dy) ;
      for(int i = 1; i <= n ;i ++ )
       if(!usedx[i]) dx[i] = 0 , q.push(i) ;
      while(q.size()) {
        int u = q.front() ;q.pop() ;
        if(depth < dx[u]) break ;
        for(auto x : v[u]) {
          if(dy[x] == -1) {
            dy[x] = dx[u] + 1 ;
            if(!usedy[x]) depth = dy[x] ;
            else dx[usedy[x]] = dy[x] + 1 , q.push(usedy[x]) ;
          }
        }
      }
      return depth != INF ;
    }
    int work()
    {
    
      memset(usedx , 0 , sizeof usedx) ;
      memset(usedy , 0 , sizeof usedy) ;
      while(bfs()) {
        memset(vis , 0 , sizeof vis) ;
        for(int i = 1 ;i <= n ;i ++ )
         if(!usedx[i] && find(i))
          ans ++ ;
      }
      cout << ans << endl ;
      return 0 ;
    }
    
    KM带权二分匹配
    int love[N][N] , ex_girl[N] , ex_boy[N] , vis_girl[N] , vis_boy[N];
    int match[N] , slack[N] , n ;
    bool dfs(int girl){
    	vis_girl[girl] = true ;
    	for(int boy = 0 ;boy < n ;boy ++){
    		if(vis_boy[boy]) continue ;
    		int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy] ;
    		if(gap == 0){
    		 	vis_boy[boy] = true ;
    		 	if(match[boy] == -1 || dfs(match[boy])) {
    			  	match[boy] = girl ;
    			  	return true ;
    			  }
    		 }
    		 else slack[boy] = min(slack[boy] , gap) ;
    	}
    	return false ;
    }
    int KM(){
    	memset(match , -1 , sizeof match) ;
    	memset(ex_boy , 0 , sizeof ex_boy) ;
    	for(int i = 0 ;i < n ;i ++)
    	 	for(int j = 0 ;j < n ;j ++)
    	 	 ex_girl[i] = max(ex_girl[i] , love[i][j]) ;
        for(int i = 0 ;i < n ;i ++)   {
         	memset(slack , INF , sizeof slack) ;
         	while(1) {
         	 	memset(vis_girl , 0 , sizeof vis_girl) ;
         	 	memset(vis_boy , 0 , sizeof vis_boy) ;
         	 	if(dfs(i)) break ;
         	 	int d = INF ;
         	 	for(int j = 0 ;j < n ;j ++)
         	 	 if(!vis_boy[j])
         	 	  d = min(d , slack[j]) ;
            for(int j = 0 ;j < n ;j ++){
         	 	 	if(vis_girl[j]) ex_girl[j] -= d ;
         	 	 	if(vis_boy[j]) ex_boy[j] += d ;
         	 	 	else slack[j] -= d ;
    			 }
    		 }
    	 }
    	 int res = 0 ;
    	 for(int i = 0 ;i < n ;i ++)
    	  res += love[match[i]][i] ;
    	 return res ;
    }
    int main(){
        while(~scanf("%d" , &n)){
         	for(int i = 0 ;i < n ;i ++)
         	 for(int j = 0 ;j < n ;j ++)
         	  scanf("%d" , &love[i][j]) ;
         	printf("%d
    " , KM()) ;
    	 }
    	return 0 ;
    }
    //O^3
    void bfs( ll k ){
        ll x , y = 0 , yy = 0 , delta;
        memset( pre , 0 , sizeof(pre) );
        for( ll i = 1 ; i <= n ; i++ ) slack[i] = INF;
        linker[y] = k;
        while(1){
            x = linker[y]; delta = INF; visy[y] = true;
            for( ll i = 1 ; i <= n ;i++ ){
                if( !visy[i] ){
                    if( slack[i] > lx[x] + ly[i] - w[x][i] ){
                        slack[i] = lx[x] + ly[i] - w[x][i];
                        pre[i] = y;
                    }
                    if( slack[i] < delta ) delta = slack[i] , yy = i ;
                }
            }
            for( ll i = 0 ; i <= n ; i++ ){
                if( visy[i] ) lx[linker[i]] -= delta , ly[i] += delta;
                else slack[i] -= delta;
            }
            y = yy ;
            if( linker[y] == -1 ) break;
        }
        while( y ) linker[y] = linker[pre[y]] , y = pre[y];
    }
    ll KM(){
        memset( lx , 0 ,sizeof(lx) );
        memset( ly , 0 ,sizeof(ly) );
        memset( linker , -1, sizeof(linker) );
        for( ll i = 1 ; i <= n ; i++ ){
            memset( visy , false , sizeof(visy) );
            bfs(i);
        }
        ll res = 0 ;
            for( ll i = 1 ; i <= n ; i++ ){
                if( linker[i] != -1 ){
                    res += w[linker[i]][i] ;
                }
            }
            return res;
    }
    int main(){
        w[i][j]=s;
        printf("%lld
    ",KM());
    }
    
    带花树求一般图最大匹配
    int findp(int x){return x==par[x]?x:par[x]=findp(par[x]);}
    int Ti=0,times[maxn]={};
    int lca(int x,int y){
       for(Ti++;times[x]!=Ti;x^=y^=x^=y)
           if(x) times[x]=Ti,x=findp(pre[link[x]]);
       return x;
    }
    void blossom(int x,int y,int p){
       while(findp(x)!=p){
           pre[x]=y;
           y=link[x];
           par[x]=par[y]=p;
           if(ty[y]==1) ty[y]=2,q.push_back(y);
           x=pre[y];
       }
    }
    bool Match(int x){
       for(int i=0;i<=n;i++) ty[i]=0,par[i]=i;
       q.clear();
       ty[x]=2,q.push_back(x);
       while(q.size()){
           x=q.front(),q.pop_front();
           for(int u:g[x])
               if(ty[u]==0){
                   ty[u]=1;
                   pre[u]=x;
                   if(link[u]) q.push_back(link[u]),ty[link[u]]=2;
                   else {
                       for(;x;u=x){
                           x=link[pre[u]];
                           link[u]=pre[u];
                           link[link[u]]=u;
                       }
                       return 1;
                   }
               }
               else if(ty[u]==2&&findp(u)!=findp(x)){
                   int p=lca(x,u);
                   blossom(x,u,p),blossom(u,x,p);
               }
       }
       return 0;
    }
    int main(){
       mem(link,0);mem(pre,0);
       ans=0;
       scanf("%d",&n);
       for(int f,t;~scanf("%d%d",&f,&t);){
           g[f].push_back(t);
           g[t].push_back(f);
       }
       for(int i=1;i<=n;i++)
           if(!link[i]&&Match(i)) ans++;
       printf("%d
    ",ans*2);
       for(int i=1;i<=n;i++)
       	  if(i<link[i])
           printf("%d %d
    ",i,link[i]);
       	return 0 ;
    }
    
    
    二分匹配单侧多重匹配
    int g[1010][1010] , vis[1010] , match[1010] , a[1010][1010]  ;
    bool find(int u , int mid) {
      for(int i = 1; i <= m ;i ++ ) {
        if(g[u][i] && !vis[i]) {
          vis[i] = 1 ;
          if(a[i][0] < mid) {
            a[i][++ a[i][0]] = u ;
            match[u] = i ;
            return true ;
          }
          else if(a[i][0] == mid) {
            for(int j = 1 ;j <= mid ;j ++ ) {
              if(find(a[i][j] , mid)){
                a[i][j] = u ;
                match[a[i][j]] = i ;
                return true ;
              }
            }
          }
        }
      }
      return false ;
    }
    // 左边n , 右边m , 右边点可用多条边
    bool check(int mid) {
      memset(match , 0 , sizeof match) ;
      memset(a , 0 , sizeof a) ;
      for(int i = 1; i <= n ;i ++ ) {
        memset(vis , 0 , sizeof vis) ;
        if(!find(i , mid)) return false ;
      }
      return true ;
    }
    int work(){
      while(cin >> n >> m) {
        if(n == 0 && m == 0) break ;
        memset(match , 0 , sizeof match) ;
        memset(g , 0 , sizeof g) ;
        for(int i = 1; i <= n ;i ++ ) {
          string s ;
          cin >> s ;
          int x ;
          char c ;
          while(scanf("%d%c" , &x , &c)) {
            g[i][x + 1] = 1 ;
            if(c == '
    ') break ;
          }
        }
        int l = 0 , r = n , ans = 0;
        while(l <= r) {
           int mid = l + r >> 1 ;
           if(check(mid)) r = mid - 1 , ans = mid ;
           else l = mid + 1 ;
        }
        cout << ans << endl ;
      }
      return 0 ;
    }
    
    一般图的最大匹配(每个点都有d[i]条边相连)
    void Push(int u){
        que[tail]=u;
        tail++;
        inque[u]=1;
    }
    int Pop(){
        int res=que[head];
        head++;
        return res;
    }
    int lca(int u,int v)//寻找公共花祖先{
        memset(inpath,0,sizeof(inpath));
        while(1){
            u=base[u];
            inpath[u]=1;
            if(u==st) break;
            u=pre[match[u]];
        }
        while(1){
            v=base[v];
            if(inpath[v]) break;
            v=pre[match[v]];
        }
        return v;
    }
    void reset(int u)//缩环{
        int v;
        while(base[u]!=newbase){
            v=match[u];
            inhua[base[u]]=inhua[base[v]]=1;
            u=pre[v];
            if(base[u]!=newbase) pre[u]=v;
        }
    }
    void contract(int u,int v)//{
        newbase=lca(u,v);
        memset(inhua,0,sizeof(inhua));
        reset(u);
        reset(v);
        if(base[u]!=newbase) pre[u]=v;
        if(base[v]!=newbase) pre[v]=u;
        for(int i=1;i<=n;i++){
            if(inhua[base[i]]){
                base[i]=newbase;
                if(!inque[i])
                    Push(i);
            }
        }
    }
    void findaug(){
        memset(inque,0,sizeof(inque));
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=n;i++)//并查集  base[i]=i;
        head=tail=1;
        Push(st);
        ed=0;
        while(head<tail){
            int u=Pop();
            for(int v=1;v<=n;v++){
                if(g[u][v]&&(base[u]!=base[v])&&match[u]!=v){
                    if(v==st||(match[v]>0)&&pre[match[v]]>0)//成环
                        contract(u,v);
                    else if(pre[v]==0){
                        pre[v]=u;
                        if(match[v]>0)  Push(match[v]);
                        else//找到增广路ed=v,return ;
                    }
                }
            }
        }
    }
    void aug(){
        int u,v,w;
        u=ed;
        while(u>0){
            v=pre[u];
            w=match[v];
            match[v]=u;
            match[u]=v;
            u=w;
        }
    }
    void edmonds()//匹配{
        memset(match,0,sizeof(match));
        for(int u=1;u<=n;u++){
            if(match[u]==0){
                st=u;
                findaug();//以st开始寻找增广路
                if(ed>0) aug();//找到增广路  重新染色,反向
            }
        }
    }
    //以上是带花树求最大匹配算法  不用看
    
    void create()//建图{
        n=0;
        memset(g,0,sizeof(g));
        for(int i=1;i<=np;i++)
            for(int j=1;j<=f[i];j++)
                mp[i][j]=++n;//拆点,给每个度的点编号
        for(int i=0;i<ne;i++)
        {//此时n+1代表x,n+2代表y
            for(int j=1;j<=f[x[i]];j++)
                g[mp[x[i]][j]][n+1]=g[n+1][mp[x[i]][j]]=1;//每个度的点与对应的x,y相连
            for(int j=1;j<=f[y[i]];j++)
                g[mp[y[i]][j]][n+2]=g[n+2][mp[y[i]][j]]=1;
            g[n+1][n+2]=g[n+2][n+1]=1;//x与y相连
            n+=2;
        }
    }
    void print(){
        ans=0;
        for(int i=1;i<=n;i++)
            if(match[i]!=0){
                  ans++;
    //            if(match[i]>i)
    //            cout<<"_____"<<i<<' '<<match[i]<<endl;
            }
        //cout<<"******"<<ans<<' '<<n<<endl;
        if(ans==n)    printf("Yes
    ");
        else    printf("No
    ");
    }
    int main(){
            while(~scanf("%d%d",&np,&ne)){
               for(int i=1;i<=np;i++)
                scanf("%d",&f[i]);
                for(int i=0;i<ne;i++)
                scanf("%d%d",&x[i],&y[i]);
                create();
                edmonds();
                print();
            }
        return 0;
    }
    

    tarjan

    void tarjan(int u){
     // dfn 节点的时间戳 , low 当前强联通分量根节点的时间戳 , ssc[i] , 当前是第i个强连通分量
      // sc 强连通分量的个数 , sz[i] 强连通分量的大小
       low[u] = dfn[u] = ++dfncnt, s[++tp] = u;
       for (auto x : v[u] {
           if (!dfn[x])
               tarjan(x), low[u] = min(low[u], low[x]);
           else if (!scc[x])
               low[u] = min(low[u], dfn[x]);
       }
       if (dfn[u] == low[u]) {
           ++sc;
           while (s[tp] != u) scc[s[tp]] = sc, sz[sc]++, --tp;
           scc[s[tp]] = sc, sz[sc]++, --tp;
       }
    }
    

    tarjan 点双连通分量

    int dfn[N] , low[N] , ans[N] , cnt = 0 ;
    vector<int> v[N] ;
    // ans[] 点所在几个点双连通分量
    void tarjan(int u , int f) {
      low[u] = dfn[u] = ++ cnt ;
      ans[u] = 0 ;
      if(f) ans[u] ++ ;
      for(auto x : v[u]) {
        if(x == f) continue ;
        if(!dfn[x]) {
          tarjan(x , u) ;
          low[u] = min(low[u] , low[x]) ;
          if(low[x] < dfn[u]) ans[u] -- ;
          ans[u] ++ ;
        }else low[u] = min(low[u] , dfn[x]) ;
      }
    }
    int work(){
      for(int i = 1; i <= n ;i ++ ) if(!dfn[i]) tarjan(i , 0) , res ++ ;
    }
    
    tarjan边双连通分量
    const int N = 5010;//点数
    const int M = 20010;//边数,因为是无向图,所以这个值要 *2
    struct Edge{
        int to,nxt;
        bool cut;//是否是桥标记
    } edge[M];
    int head[N],tot , low[N],dfn[N],sta[N],belong[N];//Belong 数组的值是1~block
    int id,top , block;//边双连通块数
    bool insta[N];
    int bridge;//桥的数目
    void add(int u,int v){
        edge[tot].to = v;
        edge[tot].nxt = head[u];
        edge[tot].cut=false;
        head[u] = tot++;
    }
    void Tarjan(int u,int pre){
        int v;
        low[u] = dfn[u] = ++id;
        sta[top++] = u;
        insta[u] = true;
        int pre_cnt = 0;
        for(int i = head[u]; i != -1; i = edge[i].nxt){
            v = edge[i].to;
            if(v == pre && pre_cnt == 0){
                pre_cnt++;
                continue;
            }
            if(!dfn[v]){
                Tarjan(v,u);
                if(low[u]>low[v]) low[u] = low[v];
                if(low[v]>dfn[u]) {
                    bridge++;
                    edge[i].cut = true;
                    edge[i^1].cut = true;
                }
            }
            else if( insta[v] && low[u] > dfn[v] )  low[u] = dfn[v];
        }
        if(low[u] == dfn[u]){
            block++;
            do{
                v = sta[--top];
                insta[v] = false;
                belong[v] = block;
            }while( v!=u );
        }
    }
    void init(){
        tot = 0;
        memset(head,-1,sizeof(head));
    }
    int du[N];//缩点后形成树,每个点的度数
    void solve(int n){
        memset(dfn,0,sizeof(dfn));
        memset(insta,false,sizeof(insta));
        id = top = block = 0;
        Tarjan(1,0);
        int ans = 0;
        memset(du,0,sizeof(du));
        for(int i = 1; i <= n; i++) for(int j = head[i]; j != -1; j = edge[j].nxt) if(edge[j].cut) du[belong[i]]++;
        for(int i = 1; i <= block; i++) if(du[i]==1) ans++; //找叶子结点的个数 ans, 构造边双连通图需要加边(ans+1)/2
        printf("%d
    ",(ans+1)/2);
    }
    int main(){
        int n,m;
        int u,v;
        while(scanf("%d%d",&n,&m)==2){
            init();
            while(m--){
                scanf("%d%d",&u,&v);
                add(u,v);
                add(v,u);
            }
            solve(n);
        }
        return 0;
    }
    

    割点与桥

    void Tarjan(int i,int Father){
        father[i]=Father;/*记录每一个点的父亲*/
        dfn[i]=low[i]=tim++;
        for(int j=0;j<G[i].size();++j) {
            int k=G[i][j];
            if(dfn[k]==-1){
                Tarjan(k,i);
                low[i]=min(low[i],low[k]);
            }
            else if(Father!=k)/*假如k是i的父亲的话,那么这就是无向边中的重边,有重边那么一定不是桥*/
                low[i]=min(low[i],dfn[k]);//dfn[k]可能!=low[k],所以不能用low[k]代替dfn[k],否则会上翻过头了。
        }
    }
    void count(){
        int rootson=0;
        Tarjan(1,0);
        for(int i=2;i<=n;++i){
            int v=father[i];
            if(v==1)
            rootson++;/*统计根节点子树的个数,根节点的子树个数>=2,就是割点*/
            else{
                if(low[i]>=dfn[v])/*割点的条件*/
                is_cut[v]=true;
            }
        }
        if(rootson>1) is_cut[1]=true;
        for(int i=1;i<=n;++i)
             if(is_cut[i]) printf("%d
    ",i);
        for(int i=1;i<=n;++i){
            int v =father[i];
            if(v>0&&low[i]>dfn[v])/*桥的条件*/
            printf("%d,%d
    ",v,i);
        }
    }
    int main(){
        input();
        memset(dfn,-1,sizeof(dfn));
        memset(father,0,sizeof(father));
        memset(low,-1,sizeof(low));
        memset(is_cut,false,sizeof(is_cut));
        count();
        return 0;
    }
    

    网络流

    最大流最小费用

    int n,m;
    int get(int x,int y)
    {
       return x*m+y;
    }
    const int maxn=100100;
    bool vis[maxn];
    int s,t,dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
    //dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
    struct Edge{
       int to,next,flow,dis;//flow流量 dis花费
    }edge[maxn];
    int head[maxn],get_edge;
    queue <int> q;
    void add(int from,int to,int flow,int dis){
       edge[++get_edge].next=head[from];
       edge[get_edge].to=to;
       edge[get_edge].flow=flow;
       edge[get_edge].dis=dis;
       head[from]=get_edge;
    }
    void add_edge(int from , int to , int flow , int dis) {
     add(from , to , flow , dis) ;
     add(from , to , 0 , -dis) ;
    }
    bool spfa(int s,int t){
       memset(dis,0x7f,sizeof(dis));
       memset(flow,0x7f,sizeof(flow));
       memset(vis,0,sizeof(vis));
       q.push(s);vis[s]=1;dis[s]=0; pre[t]=-1;
       while (!q.empty()) {
           int now=q.front();
           q.pop();
           vis[now]=0;
           for (int i=head[now]; i!=-1; i=edge[i].next){
               if (edge[i].flow>0 && dis[edge[i].to]>dis[now]+edge[i].dis)//正边{
                   dis[edge[i].to]=dis[now]+edge[i].dis;
                   pre[edge[i].to]=now;
                   last[edge[i].to]=i;
                   flow[edge[i].to]=min(flow[now],edge[i].flow);//
                   if (!vis[edge[i].to]){
                       vis[edge[i].to]=1;
                       q.push(edge[i].to);
                   }
               }
           }
       }
       return pre[t]!=-1;
    }
    void MCMF(){
       while (spfa(s,t)){
           int now=t;
           maxflow+=flow[t];
           mincost+=flow[t]*dis[t];
           //cout<<maxflow<<" "<<mincost<<endl;
           while (now!=s)
           {//从源点一直回溯到汇点
               edge[last[now]].flow-=flow[t];//flow和dis容易搞混
               edge[last[now]^1].flow+=flow[t];
               now=pre[now];
           }
       }
    }
    int main(){
       memset(head,-1,sizeof head);
       get_edge=-1;
       t=n*m+100,s=n*m+200;
       MCMF();//cout<<maxflow<<" "<<mincost<<endl;
       if(maxflow!=2)printf("-1
    ");
       else printf("%d
    ",mincost);
       return 0;
    }
    

    Dinic最大流

    struct node{
       int u, v, c, next;
    }Node[2*N];
    void add_(int u, int v, int c){
       Node[cnt].u = u;
       Node[cnt].v = v;
       Node[cnt].c = c;
       Node[cnt].next = head[u];
       head[u] = cnt++;
    }
    void add(int u, int v, int c){
       add_(u, v, c);
       add_(v, u, 0);
    }
    bool bfs(){
       queue<int> Q;
       memset(d , 0 , sizeof d) ;
       Q.push(s);
       d[s] = 1;
       while(!Q.empty()){
           int u = Q.front(); Q.pop();
           for(int i=head[u]; i!=-1; i=Node[i].next){
               node e = Node[i];
               if(!d[e.v] && e.c > 0){
                   d[e.v] = d[e.u] + 1;
                   Q.push(e.v);
                   if(e.v == t) return 1;
               }
           }
       }
       return d[t] != 0;
    }
    int dfs(int u, int cap){
       int ret = 0, V;
       if(u == t || cap == 0)
           return cap;
       for(int &i=cur[u]; i!=-1; i=Node[i].next) {
           node e = Node[i];
           if(d[e.v] == d[u] +  1 && e.c > 0){
               int V = dfs(e.v, min(cap, e.c));
               Node[i].c -= V;
               Node[i^1].c += V;
               ret += V;
               cap -= V;
               if(cap == 0) break;
           }
       }
       if(cap > 0) d[u] = -1;
       return ret;
    }
    int dinic(int u){
       int ans = 0;
       while(bfs()) {
           memcpy(cur, head, sizeof(head));
           ans += dfs(u, INF);
       }
       return ans;
    }
    void init() {
     memset(head ,-1,  sizeof head) ;
     cnt = 0 ;
    }
    

    固定流量的最小费用

    struct edge {
       int to, next, cap;
       ll cos;
    }e[M<<1];
    struct node {
       ll a, b, c;
    }p[55];
    vector<int> a[55], b;
    int n, m, cnt, s, t, k;
    void add(int u, int v, int cap, ll cos) {
       e[cnt].to = v;
       e[cnt].cap = cap;
       e[cnt].cos = cos;
       e[cnt].next = h[u];
       h[u] = cnt++;
       e[cnt].to = u;
       e[cnt].cap = 0;
       e[cnt].cos = -cos;
       e[cnt].next = h[v];
       h[v] = cnt++;
    }
    bool spfa() {
       queue<int> q;
       memset(pre, -1, sizeof pre);
       memset(vis, 0, sizeof vis);
       memset(dis, 0x3f, sizeof dis);
       dis[s] = 0;
       vis[s] = 1;
       flow[s] = INF;
       q.push(s);
       while (q.size()) {
       	int u = q.front();
       	q.pop();
       	vis[u] = 0;
       	for (int i = h[u]; ~i; i = e[i].next) {
       		int v = e[i].to;
       		if (e[i].cap && dis[v] > dis[u] + e[i].cos) {
       			pre[v] = u;
       			last[v] = i;
       			dis[v] = dis[u] + e[i].cos;
       			flow[v] = min(flow[u], e[i].cap);
       			if (!vis[v]) {
       				q.push(v);
       				vis[v] = 1;
       			}
       		}
       	}
       }
       return pre[t] != -1;
    }
    void MCMF() {
       while (spfa()) {
       	ans[++k] = ans[k-1] + 1ll*flow[t] * dis[t];
       	int now = t;
       	while (now != s) {
       		e[last[now]].cap -= flow[t];
       		e[last[now] ^ 1].cap += flow[t];
       		now = pre[now];
       	}
       }
    }
    void solve() {
       scanf("%d %d", &n, &m);
       memset(h, -1, sizeof h);
       memset(ans, 0, sizeof ans);
       cnt = 0;
       k = 0;
    // 起点,会点
       s = 0, t = N-2;
       for (int i = 1; i <= b.size(); i++) {//所有机器向汇点连边
       	add(i + n, t, 1, 0);
       }
       MCMF();
       // 固定流量的最小费用
       for (int i = 1; i <= k; i++) {
       	if (i != k) printf("%lld ", ans[i]);
       	else printf("%lld
    ", ans[i]);
       }
    }
    

    字符串

    后缀数组

    int y[N], x[N], c[N], sa[N], rk[N], height[N] , st[30][N] ;
    int get_SA() {
        memset(c, 0, sizeof(c));
        for (int i = 1; i <= n; ++i) ++c[x[i] = s[i]];
        for (int i = 2; i <= m; ++i) c[i] += c[i - 1];
        for (int i = n; i >= 1; --i) sa[c[x[i]]--] = i;
        for (int k = 1; k <= n; k <<= 1) {
            int num = 0;
            for (int i = n - k + 1; i <= n; ++i) y[++num] = i;
            for (int i = 1; i <= n; ++i) if (sa[i] > k) y[++num] = sa[i] - k;
            for (int i = 1; i <= m; ++i) c[i] = 0;
            for (int i = 1; i <= n; ++i) ++c[x[i]];
            for (int i = 2; i <= m; ++i) c[i] += c[i - 1];
            for (int i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
            swap(x, y);
            x[sa[1]] = 1;
            num = 1;
            for (int i = 2; i <= n; ++i)
                x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
            if (num == n) break;
            m = num;
    
        }
        return 0;
    }
    int get_height() {
        int k = 0;
        for (int i = 1; i <= n; ++i) rk[sa[i]] = i;
        for (int i = 1; i <= n; ++i) {
            if (rk[i] == 1) continue;
            if (k) --k;
            int j = sa[rk[i] - 1];
            while (j + k <= n && i + k <= n && s[i + k] == s[j + k]) ++k;
            height[rk[i]] = k;
        }
        return 0 ;
    }
    void build_st() {
        for (int i = 1; i <= n; i++) st[0][i] = height[i];
        for (int k = 1; k <= 19; k++) {
            for (int i = 1; i + (1 << k) - 1 <= n; i++) {
                st[k][i] = min(st[k - 1][i], st[k - 1][i + (1 << k - 1)]);
            }
        }
        return ;
    }
    int lcp(int x, int y) {
        int l = rk[x], r = rk[y];
        if (l > r) swap(l, r);
        if (l == r) return n - x + 1;
        int t = log2(r - l);
        return min(st[t][l + 1], st[t][r - (1 << t) + 1]);
    }
    struct node {
      int l , r , sum ;
    }t[N * 4];
    int tot = 0 ;
    void up(int now) {
      t[now].sum = t[t[now].l].sum + t[t[now].r].sum ;
    }
    void build(int &now , int l , int r) {
      t[now = ++ tot].sum = 0 ;
      if(l == r) return ;
      int mid = l + r >> 1 ;
      build(t[now].l , l , mid) ;
      build(t[now].r , mid + 1 , r) ;
      return ;
    }
    void update(int &now , int last , int l , int r , int pos) {
      t[now = ++ tot] = t[last] ;
      t[now].sum ++ ;
      if(l == r) return ;
      int mid = l + r >> 1 ;
      if(pos <= mid) update(t[now].l , t[last].l , l , mid , pos) ;
      else update(t[now].r , t[last].r , mid + 1 , r , pos) ;
      return ;
    }
    int ask(int now , int last , int l , int r , int k) {
      if(l == r) return l ;
      int sum = t[t[now].l].sum - t[t[last].l].sum ;
      int mid = l + r>> 1 ;
      if(sum >= k) return ask(t[now].l , t[last].l , l , mid , k) ;
      else return ask(t[now].r , t[last].r , mid + 1 , r , k - sum) ;
    }
    int root[N] ;
    int work(){
      int q ;
      tot = 0 ;
      scanf("%d%d" , &n , &q) ;
      scanf("%s" , s + 1) ;
      m = 123 ;
      get_SA() ;
      get_height() ;
      build_st() ;
      build(root[0] , 1,  n ) ;
      for(int i = 1; i <= n; i ++ ) update(root[i] , root[i - 1] , 1 , n , sa[i]) ;
      while(q --) {
        int l , r , k ;
        scanf("%d%d%d" , &l , &r , &k) ;
        int len = r - l + 1 ;
        int left = 1 , right = rk[l] ;
        int ans = rk[l] ;
        while(left <= right) {
          int mid = left + right >> 1 ;
          if(lcp(sa[mid] , l) >= len) right = mid - 1 , ans = mid ;
          else left = mid + 1 ;
        }
        int LL = ans ;
        ans = rk[l] ;
        right = n ;
        left = rk[l] ;
        while(left <= right) {
          int mid = left + right >> 1 ;
          if(lcp(sa[mid] , l) >= len) left = mid + 1 , ans = mid ;
          else right = mid - 1 ;
        }
        int RR = ans ;
        if(RR - LL + 1 < k) puts("-1") ;
        else printf("%d
    " , ask(root[RR] , root[LL - 1] , 1 , n , k)) ;
      }
      return 0 ;
    }
    

    AC自动机

    struct Trie{
     int t[N][26] , fail[N] , cnt[N] , in[N] , q[N] , h , tail , tot ;
     void init() {
       tot = 0 ;
       memset(fail , 0 , sizeof fail) ;
       memset(t , 0 , sizeof t) ;
       memset(cnt , 0 , sizeof cnt) ;
       memset(q , 0 , sizeof q) ;
     }
     int ins(char *x) {
       int i = 0 ;
       for(int j = 0 ;x[j] ;j ++ ) {
         int c = x[j] - 'a' ;
         if(!t[i][c]) t[i][c] = ++ tot ;
         i = t[i][c] ;
       }
       return i ;
     }
     void get_fail() {
       h = 1 ,tail = 0 ;
       for(int i = 0 ;i < 26 ;i ++ ) if(t[0][i]) q[++ tail] = t[0][i] ;
       while(h <= tail) {
         int i = q[h ++] ;
         for(int j = 0 ;j < 26 ;j ++ ) {
           if(t[i][j]) {
             fail[t[i][j]] = t[fail[i]][j] ;
             q[++ tail] = t[i][j] ;
           }else {
             t[i][j] = t[fail[i]][j] ;
           }
         }
       }
     }
     void solve() {
       for(int i = tail ;i > 0 ;i --) cnt[fail[q[i]]] += cnt[q[i]] ;
     }
    }T;
    char x[510][510] ;
    char s[N] ;
    int id[N] ;
    int work()
    {
     int n , m , q ;
     scanf("%d" , &n) ;
     T.init() ;
     for(int i = 1; i <= n; i ++ ) {
       scanf("%s" , s) ;
       id[i] = T.ins(s) ;
     }
     T.get_fail() ;
     scanf("%s" , s) ;
     int p = 0 ;
     for(auto x : s)
      p = T.t[p][x - 'a'] , T.cnt[p] ++ ;
     T.solve() ;
     int ans = 0 ;
     for(int i = 1; i <= n ;i ++ )
       ans += T.cnt[id[i]] ;
     cout << ans << endl ;
     return 0 ;
    }
    

    字符串最小表示法

    int getmin(int *a){
       static int b[12] ;
       for(int i = 0 ;i < 12 ;i ++) b[i] = a[i % 6] ;
       int i = 0 , j = 1 , k ;
       while(i < 6 && j < 6){
            for(k = 0 ;k < 6 && b[j + k] == b[i + k] ; k ++)  ;
            if(k == 6) break ;
            if(b[i + k] > b[j + k]){
                  i += k + 1 ;
                  if(i == j) i ++ ;
              }
            if(b[i + k] < b[j + k]){
                 j += k + 1 ;
                 if(i == j) j ++ ;
             }
        }
        k = min(i , j) ;
        for(int i = 0 ; i < 6 ;i ++)
         a[i] = b[i + k] ;
        return 0 ;
    }
    

    马拉车算法

    void init(int l , int r){
       int k=0;
       str[k++] = '$';
       for(int i=l;i<=r;i++){
           str[k++]='#';
           str[k++]=s[i];
       }
       str[k++]='#';
       len=k;
       str[k] = '' ;
    }
    int ans = 0 , flag ;
    int Manacher(){
     Len[0] = 0;
     int sum = 0;
     mx = 0;
     id = 0 ;
     // cout << str << endl ;
     for(int i=1;i<len;i++){
       if(i < mx) Len[i] = Min(mx - i, Len[2 * id - i]);
       else Len[i] = 1;
       while(str[i - Len[i]]== str[i + Len[i]]) Len[i]++;
       if(Len[i] + i > mx){        // 更新最长的回文串
         mx = Len[i] + i;          // mx是回文串右边一个位置
         id = i;             //id是回文串的中心
         sum = Max(sum, Len[i]);         // sum 是回文串的长度 + 1
    
       }
       // cout << i << " " << Len[i] << endl ;
       // if(Len[i] == i)  表示前缀是回文的
       //  {
       //    if(ans < Len[i])
       //     ans = Len[i] - 1 , flag = 1 ;
       //  }
       // if(Len[i] + i == len)  表示后缀是回文的
       //  if(ans < Len[i])
       //   ans = Len[i] - 1 , flag = 2 ;
     }
     return sum - 1 ;
    }
    

    kmp

    void get_next()
    {
       for(int i = 2, j = 0 ;i <= n;i ++)
       {
           while(j && p[i] != p[j + 1]) j = nextp[j];
           if(p[i] == p[j + 1]) j ++ ;
           nextp[i] = j ;
       }
    }
    int main()
    {
     // 在s串里面查找
       cin >> s + 1 ;
       m = strlen(s + 1);
       cin >> p + 1 ;
       n = strlen(p + 1) ;
       get_next();
       int x = 0 ;
       for(int i = 1 , j = 0;i <= m;i ++){
           while(j && s[i] != p[j + 1]) j = nextp[j] ;
           if(s[i] == p[j + 1]) j ++ ;
           if(j == n){
             //  cout << i - n << " " ;
               x ++ ;
               j = nextp[j] ;
           }
       }
       cout << x << endl  ;
       return 0;
    }
    

    数据结构

    字典树

    区间内小于某个数的所有数之和
    int n , m , root[N * 20] ;
    ll a[N] , bin[N] ;
    struct node {
     ll sum , l , r ;
    }t[N * 40];
    void fj(ll x) {
     if(x > 1000000000ll) x = 1000000001ll ;
     for(int i = 0 ;i <= 30 ;i ++ )
      bin[i] = (x >> i) & 1ll ;
    }
    int tot = 0 ;
    void insert(int &now , int last , int id , ll w) {
     t[now = ++ tot] = t[last] ;
     if(id < 0) {
       t[now].sum += w ;
       return ;
     }
     if(!bin[id]) insert(t[now].l , t[last].l , id - 1 , w) ;
     else insert(t[now].r , t[last].r , id - 1 , w) ;
     t[now].sum = t[t[now].l].sum + t[t[now].r].sum ;
    }
    // 贪心的走
    ll ask(int rt , int id) {
     if(id < 0 || !rt) return t[rt].sum ;
     ll res = 0 ;
     if(!bin[id]) res = ask(t[rt].l , id - 1) ;
     else res = t[t[rt].l].sum + ask(t[rt].r , id - 1) ;
     return res ;
    }
    int work()
    {
     scanf("%d%d" , &n , &m) ;
     for(int i = 1; i <= n ;i ++ ) {
       scanf("%d" , &a[i]) ;
       fj(a[i]) ;
       insert(root[i] , root[i - 1] , 30 , a[i]) ;
     }
     for(int i = 1; i <= m ;i ++ ) {
       int l , r ;
       scanf("%d%d" , &l , &r) ;
       ll sum = 1 , last = 0 ;
       while(1) {
         fj(sum) ;
         ll temp = ask(root[r] , 30) - ask(root[l - 1] , 30) ;
         if(temp == last) {
           printf("%lld
    " , sum) ;
           break ;
         }
         last = temp , sum = temp + 1 ;
       }
     }
     return 0 ;
    }
    
    

    主席树

    主席树求区间小于某个数的个数
    int root[N] , tot ;
    struct node {
    int l , r , sum ;
    }t[N];
    void build(int &now , int l , int r) {
    t[now = ++ tot].sum = 0 ;
    if(l == r) return ;
    int mid = l + r >> 1 ;
    build(t[now].l , l , mid) ;
    build(t[now].r , mid + 1 , r) ;
    return ;
    }
    void up(int now) {
    t[now].sum = t[t[now].l].sum + t[t[now].r].sum ;
    }
    void update(int &now , int last , int l , int r , int pos) {
    t[now = ++ tot] = t[last] ;
    if(l == r) {
      t[now].sum ++ ;
      return ;
    }
    int mid = l + r >> 1 ;
    if(pos <= mid) update(t[now].l , t[last].l , l , mid , pos) ;
    else update(t[now].r , t[last].r , mid + 1 , r , pos) ;
    up(now) ;
    return ;
    }
    int ask(int now , int last , int l , int r , int k) {
    if(!k) return 0 ;
    if(r <= k) return  t[now].sum - t[last].sum ;
    int mid = l + r >> 1 ;
    int ans = 0 ;
    if(k <= mid) ans += ask(t[now].l , t[last].l , l , mid , k) ;
    else ans += ask(t[now].l , t[last].l , l , mid , k) + ask(t[now].r , t[last].r , mid + 1 , r , k ) ;
    return ans ;
    }
    int ca = 0 ;
    int work()
    {
    int n , m ;
    scanf("%d%d" , &n , &m) ;
    for(int i = 1; i <= n ;i ++ ) scanf("%d" , &a[i]) , b[i] = a[i] ;
    sort(b + 1 , b + n + 1) ;
    cnt = unique(b + 1,  b + n + 1) - b - 1 ;
    for(int i = 1; i <= n ;i ++ ) a[i] = lower_bound(b + 1 , b + cnt + 1 , a[i]) - b ;
    tot = 0 ;
    build(root[0] , 1 , cnt) ;
    for(int i = 1 ; i <= n ;i ++ )
      update(root[i] , root[i - 1] , 1 , cnt , a[i]) ;
    printf("Case #%d:
    " , ++ ca) ;
    for(int i = 1; i <= m ;i ++ ) {
      int l , r , x , y ;
      scanf("%d%d%d%d" , &l , &r , &x , &y) ;
      y = upper_bound(b + 1 , b + cnt + 1 , y) - b - 1 ;
      x = upper_bound(b + 1 , b + cnt + 1 , x - 1) - b - 1;
      printf("%d
    " , ask(root[r] , root[l - 1] , 1 , cnt , y) - ask(root[r] , root[l - 1] , 1 , cnt , x)) ;
    }
    return 0 ;
    }
    
    主席树求区间第k小(静态)
    struct Tree {
      int l, r, sum;
    } T[MAXN * 40];
    vector<int> v ;
    int cnt, root[MAXN], a[MAXN];
    void Init() 
    {
      cnt = 0;
      T[cnt].l = 0;
      T[cnt].r = 0;
      T[cnt].sum = 0;
      root[cnt] = 0;
      v.clear();
    }
    int getid(int x) 
    { 
       return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; 
    }
    void Update(int l, int r, int &x, int y, int pos) 
    {
      T[++cnt] = T[y], T[cnt].sum++, x = cnt;
      if (l == r) return;
      int mid = (l + r) >> 1;
      if (mid >= pos)
          Update(l, mid, T[x].l, T[y].l, pos);
      else
          Update(mid + 1, r, T[x].r, T[y].r, pos);
    }
    int Query(int l, int r, int x, int y, int k) 
    {
      if (l == r) return l;
      int mid = (l + r) >> 1;
      int sum = T[T[y].l].sum - T[T[x].l].sum;
      if (sum >= k)
          return Query(l, mid, T[x].l, T[y].l, k);
      else
          return Query(mid + 1, r, T[x].r, T[y].r, k - sum);
    }
    int main() 
    {
      Init();
      int n, m;
      scanf("%d%d", &n, &m);
      for (int i = 1; i <= n; i++) 
          scanf("%d", &a[i]),v.push_back(a[i]);
      sort(v.begin(), v.end());
      v.erase(unique(v.begin(), v.end()), v.end());
      for (int i = 1; i <= n; i++)
          Update(1, n, root[i], root[i - 1], getid(a[i]));
      int l, r, k;
      for (int i = 1; i <= m; i++) 
      {
          scanf("%d%d%d", &l, &r, &k);
          printf("%d
    ", v[Query(1, n, root[l - 1], root[r], k) - 1]);
      }
      return 0;
    }
    
    主席树求区间小于某个数所有数的和
    struct node {
    ll sum ;
    int l , r ;
    }t[N * 40];
    void fj(ll x) {
    if(x > 1000000000ll) x = 1000000001ll ;
    for(int i = 0 ;i <= 30 ;i ++ )
     bin[i] = (x >> i) & 1ll ;
    }
    int tot = 0 ;
    void insert(int &now , int last , int id , ll w) {
    t[now = ++ tot] = t[last] ;
    if(id < 0) {
      t[now].sum += w ;
      return ;
    }
    if(!bin[id]) insert(t[now].l , t[last].l , id - 1 , w) ;
    else insert(t[now].r , t[last].r , id - 1 , w) ;
    t[now].sum = t[t[now].l].sum + t[t[now].r].sum ;
    }
    // 贪心的走
    ll ask(int rt , int id) {
    if(id < 0 || !rt) return t[rt].sum ;
    ll res = 0 ;
    if(!bin[id]) res = ask(t[rt].l , id - 1) ;
    else res = t[t[rt].l].sum + ask(t[rt].r , id - 1) ;
    return res ;
    }
    int work()
    {
    scanf("%d%d" , &n , &m) ;
    for(int i = 1; i <= n ;i ++ ) {
      scanf("%d" , &a[i]) ;
      fj(a[i]) ;
      insert(root[i] , root[i - 1] , 30 , a[i]) ;
    }
    for(int i = 1; i <= m ;i ++ ) {
      int l , r ;
      scanf("%d%d" , &l , &r) ;
      ll sum = 1 , last = 0 ;
      while(1) {
        fj(sum) ;
        ll temp = ask(root[r] , 30) - ask(root[l - 1] , 30) ;
        if(temp == last) {
          printf("%lld
    " , sum) ;
          break ;
        }
        last = temp , sum = temp + 1 ;
      }
    }
    return 0 ;
    }
    

    虚树

    void dfs(int u){
       dfn[u] = ++idx;
       deep[u] = deep[fa[u][0]] + 1;
       for(int e : RG[u]){
           int v = U[e] ^ V[e] ^ u;
           if(v == fa[u][0]) continue;
           me[v] = C[e];
           if(u != 1 && me[u] < me[v]) me[v] = me[u];
           fa[v][0] = u;
           dfs(v);
       }
    }
    int LCA(int u,int v){
       if(deep[u] < deep[v]) swap(u,v);
       int delta = deep[u] - deep[v];
       for(int i = 19;i >= 0;--i){
           if((delta >> i) & 1) u = fa[u][i];
       }
       for(int i = 19;i >= 0;--i){
           if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
       }
       if(u == v) return u;
       return fa[u][0];
    }
    bool comp(int a,int b){
       return dfn[a] < dfn[b];
    }
    void insert(int u){
       if(top == 1) {stk[++top] = u;return;}
       int lca = LCA(u,stk[top]);
       if(lca == stk[top]) {stk[++top] = u;return ;}
       while(top > 1 && dfn[lca] <= dfn[stk[top-1]]){
           VG[stk[top-1]].push_back(stk[top]);
           --top;
       }
       if(lca != stk[top]) {
           VG[lca].push_back(stk[top]);
           stk[top] = lca;
       }
       stk[++top] = u;
    }
    int idq[maxn],mark[maxn];
    ll DP(int u){
       ll cost = 0;
       for(int v : VG[u])
           cost += min(me[v],DP(v));
       VG[u].clear();
       if(mark[u]) return me[u];
       else return cost;
    }
    int main(){
       init();
       ios::sync_with_stdio(false);
       cin >> n;
       for(int i = 1;i < n;++i){
           cin >> U[i] >> V[i] >> C[i];
           RG[U[i]].push_back(i);
           RG[V[i]].push_back(i);
       }
       dfs(1);
       for(int t = 1;t <= 19;++t) for(int i = 1;i <= n;++i){
           fa[i][t] = fa[fa[i][t-1]][t-1];
       }
       cin >> m;
       for(int i = 0;i < m;++i){
           int sz;
           cin >> sz;
           for(int j = 0;j < sz;++j){
               cin >> idq[j];
               mark[idq[j]] = 1;
           }
           sort(idq,idq+sz,comp);
           top = 0;
           stk[++top] = 1;
           for(int j = 0;j < sz;++j) insert(idq[j]);
           while(top > 0) {
               VG[stk[top-1]].push_back(stk[top]);
               top--;
           }
           cout << DP(1) << endl;
           for(int j = 0;j < sz;++j) VG[idq[j]].clear(),mark[idq[j]] = 0;
           VG[0].clear();
       }
       return 0;
    }
    

    线段树

    线段树板子乘加
    //线段树操作   区间最大值  单点修改  单点查询
    //             区间修改  区间查询   最大连续和
    
    #include <iostream>
    using namespace std;
    const int N = 1e5 + 10 ;
    typedef long long ll;
    int a[N];
    struct node{
      ll muti , add , v;
    }tree[4 * N];
    int n , m , mod;
    void pushdown(int root ,int l,int r){
      int mid = l + r >> 1;
      tree[root * 2].v =(tree[root * 2].v * tree[root].muti + tree[root].add * (mid - l + 1)) % mod;
      tree[root * 2 + 1].v =(tree[root * 2 + 1].v * tree[root].muti + tree[root ].add * (r - mid)) % mod;
      tree[root * 2].muti =(tree[root * 2].muti * tree[root].muti) % mod ;
      tree[root * 2 + 1].muti =(tree[root * 2 + 1].muti * tree[root].muti) % mod ;
      tree[root * 2].add =(tree[root * 2].add * tree[root].muti + tree[root].add) % mod ;
      tree[root * 2 + 1].add =(tree[root * 2 + 1].add * tree[root].muti + tree[root].add) % mod ;
      tree[root].muti = 1 ,tree[root].add = 0;
      return ;
    }
    void build(int root ,int l,int r){
      tree[root].add = 0,tree[root].muti = 1;
      if(l == r){
      	tree[root].v = a[l] ; return ;
      }
      int mid  = l + r >> 1 ;
      build(root * 2 , l, mid) ,build(root * 2 + 1,mid + 1 ,r );
      tree[root].v = (tree[root * 2].v + tree[root * 2 + 1].v ) % mod;
      return ;
    
    }
    void change1(int root ,int treel,int treer ,int x, ll k){//单点修改   将第x 个修改为 k
      tree[root].v += k;
      int mid = treel + treer >> 1;
      if(x > mid) change1(root * 2 + 1,mid + 1,treer , x , k);
      else change1(root * 2 , treel,mid , x , k);
      tree[root].v = (tree[root * 2].v + tree[root * 2 + 1].v) % mod ;
      return ;
    }
    int query1(int root,int treel,int treer,int x){ // 单点查询   第x个点
      if(treel == treer) return tree[root].v ;
      int mid = treel + treer >> 1;
      if(x <= mid) query1(root * 2 , treel ,mid ,x);
      else query1(root * 2 + 1 ,mid + 1 ,treer ,x) ;
    }
    void change2(int root,int treel,int treer,int l,int r,ll k){ // 区间修改   乘法   x - y区间乘上 k
      if(l >treer || r < treel) return ;
      if(l <= treel && treer <= r){
      	tree[root].v =(tree[root].v * k) % mod ;
      	tree[root].muti = (tree[root].muti * k) % mod ;
      	tree[root].add = (tree[root].add * k) % mod ;
      	return ;
      }
      pushdown(root,treel,treer);
      int mid = treel + treer >> 1 ;
      change2(root * 2,treel,mid , l ,r ,k);
      change2(root * 2 + 1,mid + 1,treer, l ,r ,k);
      tree[root].v = (tree[root * 2].v + tree[root * 2 + 1].v) % mod ;
      return ;
    }
    void change3(int root,int treel,int treer,int l,int r,ll k){ // 区间修改  加法  x- y区间加上k
      if(l >treer || r < treel) return ;
      if(l <= treel && treer <= r){
      	tree[root].v =(tree[root].v + k*(treer- treel + 1)) % mod ;
      	tree[root].add = (tree[root].add + k) % mod ;
      	return ;
      }
      pushdown(root,treel,treer);
      int mid = treel + treer >> 1 ;
      change3(root * 2,treel,mid , l ,r ,k);
      change3(root * 2 + 1,mid + 1,treer, l ,r ,k);
      tree[root].v = (tree[root * 2].v + tree[root * 2 + 1].v) % mod ;
      return ;
    }
    
    ll query2(int root,int treel,int treer,int l,int r){ // 区间求和  求区间x - y 的和
      if(l >treer || r <treel) return 0;
      if(l <= treel &&treer <= r) return tree[root].v ;
      pushdown(root,treel,treer);
      int mid = treel + treer >> 1;
      return (query2(root * 2 ,treel,mid , l ,r) + query2(root * 2 + 1,mid + 1,treer ,l,r))%mod ;
    }
    
    线段树区间最值
    void up(int rt){
    minx[rt] = max(minx[ls] , minx[rs]) ;
    }
    void build(int rt , int l , int r){
    if(l == r) {
      minx[rt] = a[l] ;
      return ;
    }
    int mid = l + r >> 1 ;
    build(ls , l , mid) ;
    build(rs , mid + 1 , r) ;
    up(rt) ;
    return ;
    }
    void down(int rt , int l , int r){
    if(lazy[rt]) {
      lazy[ls] += lazy[rt] ;
      lazy[rs] += lazy[rt] ;
      minx[ls] += lazy[rt] ;
      minx[rs] += lazy[rt] ;
      minx[rt] = max(minx[ls] , minx[rs]) ;
      lazy[rt] = 0 ;
    }
    return ;
    }
    void add(int rt , int l , int r , int ql , int qr , ll k){
    if(ql <= l && r <= qr) {
      minx[rt] += k ;
      lazy[rt] += k ;
      return ;
    }
    down(rt , l , r) ;
    int mid = l + r >> 1 ;
    if(ql <= mid) add(ls , l , mid , ql , qr , k) ;
    if(qr > mid) add(rs , mid + 1 , r , ql , qr , k) ;
    up(rt) ;
    return ;
    }
    ll ask(int rt , int l , int r , int ql , int qr){
    if(ql <= l && r <= qr) return minx[rt] ;
    down(rt , l , r) ;
    ll ans = 0 ;
    int mid = l + r >> 1 ;
    if(ql <= mid) ans = max(ans , ask(ls , l , mid , ql , qr)) ;
    if(qr > mid) ans = max(ans , ask(rs , mid + 1 , r , ql , qr)) ;
    return ans ;
    }
    
    线段树维护矩阵乘法
    struct node {
    ll a[2][2] ;
    } ;
    char a[N] ;
    node muti(node a , node b){
    node res ;
    for(int i = 0 ;i < 2; i ++ )
     for(int j = 0 ;j < 2 ;j ++ )
      res.a[i][j] = 0 ;
    for(int k = 0 ;k < 2; k ++ )
     for(int i = 0 ;i < 2; i ++ )
      for(int j = 0 ;j < 2; j ++ )
        res.a[i][j] = (res.a[i][j] + a.a[i][k] * b.a[k][j] % mod ) % mod ;
    return res ;
    }
    struct Node {
    int l , r , lazy ;
    node ans , rans ;
    }tr[N];
    void up(int rt){
    tr[rt].ans = muti(tr[ls].ans , tr[rs].ans) ;
    tr[rt].rans = muti(tr[ls].rans , tr[rs].rans) ;
    return ;
    }
    void down(int rt){
    if(tr[rt].lazy) {
      swap(tr[ls].ans , tr[ls].rans) ;
      swap(tr[rs].ans , tr[rs].rans) ;
      tr[ls].lazy ^= 1 ;
      tr[rs].lazy ^= 1 ;
      tr[rt].lazy ^= 1 ;
    }
    return ;
    }
    void build(int rt , int l , int r){
    tr[rt].l = l , tr[rt].r = r ;
    tr[rt].lazy = 0 ;
    if(l == r) {
      if(a[l] == 'A')  {
        tr[rt].ans.a[0][0] = 1 , tr[rt].ans.a[0][1] = 0 ;
        tr[rt].ans.a[1][0] = 1 , tr[rt].ans.a[1][1] = 1 ;
        tr[rt].rans.a[0][0] = 1 , tr[rt].rans.a[0][1] = 1 ;
        tr[rt].rans.a[1][0] = 0 , tr[rt].rans.a[1][1] = 1 ;
      }else {
        tr[rt].ans.a[0][0] = 1 , tr[rt].ans.a[0][1] = 1 ;
        tr[rt].ans.a[1][0] = 0 , tr[rt].ans.a[1][1] = 1 ;
        tr[rt].rans.a[0][0] = 1 , tr[rt].rans.a[0][1] = 0 ;
        tr[rt].rans.a[1][0] = 1 , tr[rt].rans.a[1][1] = 1 ;
      }
      return ;
    }
    int mid = l + r >> 1 ;
    build(ls , l , mid) ;
    build(rs , mid + 1 , r) ;
    up(rt) ;
    return ;
    }
    void update(int rt , int l , int r){
    if(l <= tr[rt].l && tr[rt].r <= r) {
      tr[rt].lazy ^= 1 ;
      swap(tr[rt].ans , tr[rt].rans) ;
      return ;
    }
    down(rt) ;
    int mid = tr[rt].l + tr[rt].r >> 1 ;
    if(l <= mid) update(ls , l , r) ;
    if(r > mid) update(rs , l , r) ;
    up(rt) ;
    return ;
    }
    node ask(int rt , int l , int r){
    if(tr[rt].l >= l && tr[rt].r <= r) return tr[rt].ans ;
    down(rt) ;
    int mid = tr[rt].l + tr[rt].r >> 1 ;
    node ans ;
    ans.a[0][0] = 1 , ans.a[0][1] = 0 ;
    ans.a[1][0] = 0 , ans.a[1][1] = 1 ;
    if(l <= mid) ans = muti(ans , ask(ls , l , r)) ;
    if(r > mid) ans = muti(ans , ask(rs , l , r)) ;
    up(rt) ;
    return ans ;
    }
    
    扫描线
    const int MAX=20000 ;
    int mark[MAX<<2];//记录某个区间的下底边个数
    double sum[MAX<<2];//记录某个区间的下底边总长度
    double Hash[MAX];//对x进行离散化,否则x为浮点数且很大无法进行线段树
    //以横坐标作为线段(区间),对横坐标线段进行扫描
    //扫描的作用是每次更新下底边总长度和下底边个数,增加新面积
    struct seg{//线段
      double l,r,h;
      int d;
      seg(){}
      seg(double x1,double x2,double H,int c):l(x1),r(x2),h(H),d(c){}
      bool operator<(const seg &a)const{
      	return h<a.h;
      }
    }s[MAX];
    void Upfather(int n,int left,int right){
      if(mark[n])sum[n]=Hash[right+1]-Hash[left];//表示该区间整个线段长度可以作为底边
      else if(left == right)sum[n]=0;//叶子结点则底边长度为0(区间内线段长度为0)
      else sum[n]=sum[n<<1]+sum[n<<1|1];
    }
    void Update(int L,int R,int d,int n,int left,int right){
      if(L<=left && right<=R){//该区间是当前扫描线段的一部分,则该区间下底边总长以及上下底边个数差更新
      	mark[n]+=d;//更新底边相差差个数
      	Upfather(n,left,right);//更新底边长
      	return;
      }
      int mid=left+right>>1;
      if(L<=mid)Update(L,R,d,n<<1,left,mid);
      if(R>mid)Update(L,R,d,n<<1|1,mid+1,right);
      Upfather(n,left,right);
    }
    int search(double key,double* x,int n){
      int left=0,right=n-1;
      while(left<=right){
      	int mid=left+right>>1;
      	if(x[mid] == key)return mid;
      	if(x[mid]>key)right=mid-1;
      	else left=mid+1;
      }
      return -1;
    }
    int main(){
      int n,num=0;
      double x1,x2,y1,y2;
      while(cin>>n,n){
      	int k=0;
      	for(int i=0;i<n;++i){
      		cin>>x1>>y1>>x2>>y2;
      		Hash[k]=x1;
      		s[k++]=seg(x1,x2,y1,1);
      		Hash[k]=x2;
      		s[k++]=seg(x1,x2,y2,-1);
      	}
      	sort(Hash,Hash+k);
      	sort(s,s+k);
      	int m=1;
      	for(int i=1;i<k;++i)//去重复端点
      	    if(Hash[i] != Hash[i-1])Hash[m++]=Hash[i];
          double ans=0;
          //memset(mark,0,sizeof mark);
          //memset(sum,0,sizeof sum);如果下面是i<k-1则要初始化,因为如果对第k-1条线段扫描时会使得mark,sum为0才不用初始化的
      	for(int i=0;i<k;++i){//扫描线段
      		int L=search(s[i].l,Hash,m);
      		int R=search(s[i].r,Hash,m)-1;
      		Update(L,R,s[i].d,1,0,m-1);//扫描线段时更新底边长度和底边相差个数
      		ans+=sum[1]*(s[i+1].h-s[i].h);//新增加面积
      	}
      	printf("%.0lf
    ",ans);
      }
    puts("*") ;
      return 0;
    }
    /*
    这里注意下扫描线段时r-1:int R=search(s[i].l,Hash,m)-1;
    计算底边长时r+1:if(mark[n])sum[n]=Hash[right+1]-Hash[left];
    解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
    则我们去更新的时候,会算到sum[1]=Hash[mid]-Hash[left]+Hash[right]-Hash[mid+1]
    这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
    离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如Hash[3]=1.2,Hash[4]=5.6,mid=3
    所以这里用r-1,r+1就很好理解了
    */
    
    线段树维护hash
    const int N = 1e6 + 10 , INF = 0x3f3f3f3f , p = 1e9 + 7 , mod = 65536 ;
    ll pw[N << 2] , hash1[N << 2] , hash2[N << 2] , sum[N << 2] , PW[N] ;
    int a[N] ;
    void push_up(int rt) {
     sum[rt] = (sum[ls] + sum[rs]) % mod ;
     hash1[rt] = (hash1[ls] * pw[rs] % p + hash1[rs]) % p ;
     hash2[rt] = (hash2[rs] * pw[ls] % p + hash2[ls]) % p ;
     pw[rt] = pw[ls] * pw[rs] % p ;
     return ;
    }
    void build(int rt , int l , int r) {
     if(l == r) {
       pw[rt] = 19260817 ;
       hash1[rt] = hash2[rt] = sum[rt] = a[l] ;
       return ;
     }
     int mid = l + r >> 1 ;
     build(ls , l , mid) ;
     build(rs , mid + 1 , r) ;
     push_up(rt) ;
    }
    void update(int rt , int l , int r , int p) {
     if(l == r) {
       hash1[rt] = hash2[rt] = sum[rt] = a[l] ;
       return  ;
     }
     int mid = l + r >> 1 ;
     if(p <= mid) update(ls,  l , mid , p) ;
     else update(rs , mid + 1 , r , p) ;
     push_up(rt) ;
    }
    ll ask1(int rt , int l , int r , int ql , int qr) {
     if(ql <= l && r <= qr) return sum[rt] ;
     int mid = l + r >> 1 , ans = 0 ;
     if(ql <= mid) ans = (ans + ask1(ls , l , mid , ql , qr)) % mod ;
     if(qr > mid) ans = (ans + ask1(rs , mid + 1 , r  , ql , qr)) % mod ;
     return ans ;
    }
    ll ask2(int rt , int l , int r , int ql , int qr) {
     if(ql <= l && r <= qr)
       return hash1[rt] ;
     int mid = l + r >> 1 ;
     ll ans = 0 ;
     if(ql <= mid) ans = (ans + ask2(ls , l , mid , ql , qr) * PW[mid < min(qr , r) ? min(qr , r) - mid : 0]) % p ;
     if(qr > mid) ans = (ans + ask2(rs , mid + 1 , r , ql , qr)) % p ;
     return ans ;
    }
    ll ask3(int rt , int l , int r , int ql , int qr) {
     if(ql <= l && r <= qr) return hash2[rt] ;
     int mid = l + r >> 1 ;
     ll ans = 0 ;
     if(ql <= mid)  ans = (ans + ask3(ls , l , mid , ql , qr)) % p ;
     if(qr > mid) ans = (ans + ask3(rs , mid + 1 , r , ql , qr) * PW[max(l , ql) <= mid ? mid - max(l , ql) + 1 : 0]) % p ;
     return ans ;
    }
    // 1 a[l , r] ++
    // 2 [x , x + L - 1] == [y , y + L - 1]
    int main()
    {
     int n , q ;
     memset(pw , 1 , sizeof pw) ;
     scanf("%d%d" , &n , &q) ;
     PW[0] = 1 ;
     for(int i = 1; i <= n ;i ++ )
      PW[i] = PW[i - 1] * 19260817 % p ;
     for(int i = 1; i <= n ;i ++ ) scanf("%d" , &a[i]) ;
     for(int i = n ;i >= 2; i -- ) {
       a[i] -= a[i - 1] ;
       if(a[i] < 0) a[i] += mod ;
     }
     build(1 , 1 , n) ;
     while(q --) {
       int op ;
       scanf("%d" , &op) ;
       if(op == 1) {
         int l , r ;
         scanf("%d%d" , &l , &r) ;
         a[l] = (a[l] + 1) % mod ;
         update(1 , 1 , n , l) ;
         if(r != n) {
           a[r + 1] -- ;
           if(a[r + 1] < 0) a[r + 1] += mod ;
           update(1 , 1 , n , r + 1) ;
         }
       }else {
         int x , y , L ;
         scanf("%d%d%d" , &x , &y , &L) ;
         if(x > y) swap(x , y) ;
         if(x == y) puts("yes") ;
         else if(ask1(1 , 1 , n , x + 1 , y) != 0) puts("no") ;
         else if(ask2(1 , 1 , n , x + 1 , x + L - 1) != ask2(1 , 1 , n , y + 1 , y + L - 1))
                   puts("no") ;
         else if(ask3(1 , 1 , n , x + 1 , x + L - 1) != ask3(1 , 1 , n , y + 1 , y + L - 1))
                    puts("no") ;
         else puts("yes") ;
        }
     }
     return 0 ;
    }
    
    

    树hash

    void get(){
     for(int i = 2; i < N ;i ++ ) {
       if(!vis[i]) prime[++ tot] = i ;
       for(int j = 1; j <= tot && i * prime[j] < N ;j ++ ) {
         vis[i * prime[j]] = 1 ;
         if(i % prime[j] == 0) break ;
       }
     }
    }
    ll f[N] ;
    void dfs2(int u , int fa , int rt) {
     sz[u] = 1 , f[u] = 1;
     for(auto x : v[u]) {
       if(x == fa || x == rt) continue ;
       dfs2(x , u , rt) ;
       sz[u] += sz[x] ;
       f[u] += 1ll * prime[sz[x]] * f[x] ;
     }
    }
    

    矩阵树(矩阵树定理主要用于图的生成树计数)

    //f[i][i] 存i点的度数
    //f[i][j] 存i点到j点的边数的相反数
    //ans为图的生成树数量
    void add(int u,int v){
       f[u][u]++;f[v][v]++;
       f[u][v]--;f[v][u]--;
    }
    int u[N] , v[N] , w[N];
    ll gauss () {// 生成树的数量
       ll ans = 1 ;
       for (int i = 1; i < n; i ++) {
           for (int j = i + 1; j < n; j ++) {
               while (f[j][i]) {
                   ll t = f[i][i] / f[j][i] ;
                   for (int k = i; k < n; k ++)
                       f[i][k] = (f[i][k] - t * f[j][k] + mod) % mod ;
                   swap(f[i], f[j]) ;
                   ans = -ans ;
               }
           }
           ans = (ans * f[i][i]) % mod ;
       }
       return (ans + mod) % mod ;
    }
    

    树上启发式合并

    统计当前子树内最大颜色点数和
    /*
    1. CF600E - Lomsat gelral
    题意
    N个节点以点1为根的树,每一个节点有一个颜色color[i],对于一个以u为根的子树来说,
    他的子树中的节点有多种颜色,现在求出出现次数最多的颜色(可能有多个颜色出现次数相同),并将出现次数求和
    输出以所有点的答案
    分析
    以点1为根,我们需要保存的信息为当前点子树中颜色出现次数,并且在统计答案的时候找到颜色最多的
    这里我们可以用一个桶cnt[i]来记录颜色ii出现的次数,然后用mx来记录当前次数最多的颜色,sum记录次数最多的颜色的次数之和
    这样当某种颜色出现次数cnt>mx的时候,我们将mx更新,
    并且sum=cnt (为什么不是加上答案呢,因为我们最大值已经更新了,所以实际上是sum=0, sum = sum + cnt)
    当cnt=mx的时候,mx不必更新,sum+=cnt
    当cnt<mx的时候,啥都不用做
    */
    void dfs(int u , int fa)//轻重链划分
    {
    size[u] = 1 ;
    for(auto x : v[u])
    {
      if(x == fa) continue ;
      dfs(x, u) ;
      size[u] += size[x] ;
      if(!son[u] || size[x] > size[son[u]])
       son[u] = x ;
    }
    }
    void upd(int u , int fa , int k) //将信息加入/撤回
    {
    //k = 1的时候是加上信息,k = -1的时候是撤回信息
    // 记录信息
    cnt[colour[u]] += k ;
    if(k == 1 && cnt[colour[u]] >= mx) {
       if(cnt[colour[u]] > mx) mx = cnt[colour[u]] , sum = 0 ;
       sum += colour[u] ;
     }
    for(auto x : v[u])
     if(x != fa && !vis[x]) upd(x , u , k) ;
    
    }
    void dsu(int u , int fa , int keep)//u为当前点, fa为父节点, keep为是否保留信息
    {
     for(auto x : v[u]) //先搜我们的轻儿子,过程中不保留信息
      if(x != fa && x != son[u]) dsu(x , u , 0) ;
     if(son[u]) dsu(son[u] , u  , 1) , vis[son[u]] = 1 ;
     upd(u , fa , 1) ;
     // 统计答案
     ans[u] = sum ;
     if(son[u]) vis[son[u]] = 0 ;
     if(!keep) upd(u , fa , -1) , mx = 0 , sum = 0; /*  如果信息不保留,那我们就撤回        */
    }
    
    统计当前字数内两点之间距离为k的点对
    void dfs(int u , int f){
    deep[u] = deep[f] + 1 ;
    fa[u] = f ;
    for(auto x : v[u]){
       if(x != f) {
          dfs(x , u) ;
          size[u] += size[x] ;
          if(!son[u] || size[x] > size[son[u]])
           son[u] = x ;
        }
     }
    }
    void calc(int u , int keep){
    num[deep[u]] += keep ;
    sum[deep[u]] += keep * a[u] ;
    for(auto x : v[u]) {
      if(x == fa[u] || x == nowson) continue ;
      calc(x , keep) ;
    }
    }
    void query(int u , int lca){
    int d = k + 2 * deep[lca] - deep[u] ;
    if(d > 0){
      ans[lca] += sum[d] ;
      ans[lca] += num[d] * a[u] ;
    }
    for(auto x : v[u]){
        if(x == fa[u]) continue ;
        query(x , lca) ;
    }
    }
    void dsu(int u , int fa , int keep){
    for(auto x : v[u]){
      if(x == fa || x == son[u]) continue ;
      dsu(x , u , 0) ;
    }
    if(son[u]) dsu(son[u] , u , 1) , nowson = son[u] ;
    for(auto x : v[u]){
       if(x == fa || x == nowson)  continue ;
       query(x , u) ;
       calc(x , 1) ;
     }
     num[deep[u]] += 1 ;
     sum[deep[u]] += a[u] ;
     nowson = 0 ;
     if(!keep) calc(u , -1) ;
    }
    

    数论

    中国剩余定理

    ll exgcd(ll a , ll b , ll &x , ll &y){
      if(b == 0){
       	x = 1 , y = 0 ;
       	return a ;
       }
       ll t = exgcd(b , a % b , y , x) ;
       y -= a / b * x ;
       return t ;
    }
    ll inv(ll a , ll b){
      ll x , y ;
      ll t = exgcd(a , b , x , y) ;
      while(x < 0) x += b ;
      return x ;
    }
    ll x[N] , m[N] ;
    inline ll CRT(){
    for(int i = 1; i <= 4 ;i ++ ) m[i] = prime[i] ;
    ll M = 1, ans = 0;
    for (int i = 1 ; i <= 4; i++)
      M *= m[i];
    for (int i = 1; i <= 4; i++)
      ans = (ans + x[i] * (M / m[i]) % M * inv(M / m[i], m[i]) % M) % M;
    return ans ;
    }
    

    求n以内的素数个数

    #include<cmath>
    #include<cstdio>
    #include<iostream>
    #define IL inline
    #define RG register
    using namespace std;
    typedef long long LL;
    const int N=5000005;
    const int M=7;
    const int PM=2*3*5*7*11*13*17;
    int p[N],pi[N];
    int phi[PM+1][M+1],sz[M+1];
    LL n;
    bool f[N];
    IL void getprime(){
      for(RG int i=2;i<N;i++){
          if(!f[i])
              p[++p[0]]=i;
          pi[i]=p[0];
          for(RG int j=1;p[j]*i<N;j++){
              f[p[j]*i]=1;
              if(i%p[j]==0)
                  break;
          }
      }
    }
    IL void init(){
      getprime();
      sz[0]=1;
      for(RG int i=0;i<=PM;i++)
          phi[i][0]=i;
      for(RG int i=1;i<=M;i++){
          sz[i]=p[i]*sz[i-1];
          for(RG int j=1;j<=PM;j++)
              phi[j][i]=phi[j][i-1]-phi[j/p[i]][i-1];
      }
    }
    IL int sqrt2(LL x){
      LL r=(LL)sqrt(x-0.1);
      while(r*r<=x) r++;
      return (int)(r-1);
    }
    IL int sqrt3(LL x){
      LL r=(LL)cbrt(x-0.1);
      while(r*r*r<=x) r++;
      return (int)(r-1);
    }
    IL LL getphi(LL x,int s){
      if(s==0) return x;
      if(s<=M) return phi[x%sz[s]][s]+(x/sz[s])*phi[sz[s]][s];
      if(x<=p[s]*p[s]*p[s]&&x<N){
          int s2x=pi[sqrt2(x)];
          LL ans=pi[x]-(s2x+s-2)*(s2x-s+1)/2;
          for(RG int i=s+1;i<=s2x;i++)
              ans+=pi[x/p[i]];
          return ans;
      }
      return getphi(x,s-1)-getphi(x/p[s],s-1);
    }
    IL LL getpi(LL x){
      if(x<N) return pi[x];
      LL ans=getphi(x,pi[sqrt3(3)])+pi[sqrt3(x)]-1;
      for(RG int i=pi[sqrt3(x)]+1,ed=pi[sqrt2(x)];i<=ed;i++)
          ans-=getpi(x/p[i])-i+1;
      return ans;
    }
    LL MeiLeh(LL x){
      if(x<N) return pi[x];
      int a=(int)MeiLeh(sqrt2(sqrt2(x)));
      int b=(int)MeiLeh(sqrt2(x));
      int c=(int)MeiLeh(sqrt3(x));
      LL sum=getphi(x,a)+(LL)(b+a-2)*(b-a+1)/2;
      for(int i=a+1;i<=b;i++){
          LL w=x/p[i];
          sum-=MeiLeh(w);
          if(i>c) continue;
          LL lim=MeiLeh(sqrt2(w));
          for(int j=i;j<=lim;j++)
              sum-=MeiLeh(w/p[j])-(j-1);
      }
      return sum;
    }
    

    Miller_Rabin

    #define me(x,y) memset(x,y,sizeof x)
    #define MIN(x,y) x < y ? x : y
    #define MAX(x,y) x > y ? x : y
    typedef long long ll;
    const int maxn = 1e5+10;
    const double INF = 0x3f3f3f3f;
    const int MOD = 1e6;
    const double eps = 1e-06;
    ll Quick_Multiply(ll a,ll b,ll p){  // a*b%p
      ll ans = 0;
      a %= p;
      b %= p;
      if(b < 0) a = -a,b = -b;
      while(b){
          if(b&1) ans = (ans+a)%p;
          b >>= 1;
          a = (a+a)%p;
      }
      ans = (ans+p)%p;
      return ans;
    }
    ll Quick_Pow(ll a,ll b,ll p){   //a^b%p
      ll ans = 1;
      while(b){
          if(b&1) ans = Quick_Multiply(ans,a,p)%p;
          b >>= 1;
          a = Quick_Multiply(a,a,p)%p;
      }
      return ans;
    }
    bool Miller_Rabin(ll n){    //Miller_Rabin
      ll i,j,a,x,y,t,u,s = 10;
      if(n == 2 || n == 3) return true;
      if(n < 2 || !(n&1)) return false;
      for(t = 0,u = n-1;!(u&1); t++,u>>=1);   //n-1 = u*2^t
      for(i = 0; i < s; i++){
          a = rand()%(n-1)-1;
          x = Quick_Pow(a,u,n);   //a^u
          for(j = 0; j < t; j++){
              y = Quick_Multiply(x,x,n);  //x^2
              if(y == 1 && x != 1 && x != n-1) return false;  //����̽��
              x = y;
          }
          if(x != 1) return false;    //��������
      }
      return true;
    }
    

    Pollard_rho

    ll factor[maxn];
    int tot;
    ll muti_mod(ll a,ll b,ll c){    //����(a*b) mod c,a,b,c<2^63
      a%=c , b%=c;
      ll ret=0;
      while (b){
          if (b&1){
              ret+=a;
              if (ret>=c) ret-=c;
          }
          a<<=1;
          if (a>=c) a-=c;
          b>>=1;
      }
      return ret;
    }
    ll pow_mod(ll x,ll n,ll mod){  //����x^n mod c ,�ǵݹ���
      if (n==1) return x%mod;
      int bit[64],k=0;
      while (n){
          bit[k++]=n&1;
          n>>=1;
      }
      ll ret=1;
      for (k=k-1;k>=0;k--){
          ret=muti_mod(ret,ret,mod);
          if (bit[k]==1) ret=muti_mod(ret,x,mod);
      }
      return ret;
    }
    bool check(ll a,ll n,ll x,ll t){   //��aΪ����n-1=x*2^t������n�Dz��Ǻ���
      ll ret=pow_mod(a,x,n),last=ret;
      for (int i=1;i<=t;i++){
          ret=muti_mod(ret,ret,n);
          if (ret==1 && last!=1 && last!=n-1) return 1;
          last=ret;
      }
      if (ret!=1) return 1;
      return 0;
    }
    bool Miller_Rabin(ll n){
      ll x=n-1,t=0;
      while ((x&1)==0) x>>=1,t++;
      bool flag=1;
      if (t>=1 && (x&1)==1){
          for (int k=0;k<S;k++){
              ll a=rand()%(n-1)+1;
              if (check(a,n,x,t)) {flag=1;break;}
              flag=0;
          }
      }
      if (!flag || n==2) return 0;
      return 1;
    }
    ll gcd(ll a,ll b){
      if (a==0) return 1;
      if (a<0) return gcd(-a,b);
      while (b){
          ll t=a%b; a=b; b=t;
      }
      return a;
    }
    ll Pollard_rho(ll x,ll c){
      ll i=1,x0=rand()%x,y=x0,k=2;
      while (1){
          i++;
          x0=(muti_mod(x0,x0,x)+c)%x;
          ll d=gcd(y-x0,x);
          if (d!=1 && d!=x){
              return d;
          }
          if (y==x0) return x;
          if (i==k){
              y=x0;
              k+=k;
          }
      }
    }
    void findfac(ll n){           //�ݹ������������ֽ�N
      if (!Miller_Rabin(n)){
          factor[tot++] = n;
          return;
      }
      ll p=n;
      while (p>=n) p=Pollard_rho(p,rand() % (n-1) +1);
      findfac(p);
      findfac(n/p);
    }
    int idx = 0 ;
    struct node
    {
      ll val , num ;
      node() {}
      node(ll val , ll num) : val(val) , num(num) {} 
      bool operator<(const node &a) const 
      {
      	val < a.val ;
      }
    }fac[maxn];
    void solve(ll n)
    {
      if (!Miller_Rabin(n)) printf("Prime
    ");
      else{
              tot = 0;
              findfac(n);
              sort(factor , factor + tot) ;
              idx = 0 ;
              for(int i = 0 ; i < tot ;i ++)
               {
               	if(factor[i] != fac[idx].val) 
               	 fac[++ idx] = {factor[i] , 1};
               	else 
               	 fac[idx].num ++ ;
      		 }
          }
    }
    int main(){
      srand(time(NULL));
      int t;
      scanf("%d",&t);
      while (t--){
          ll n;
          scanf("%I64d",&n);
      	solve(n) ;
          for(int i = 1; i <= idx ;i ++)
           cout << fac[i].val << " " << fac[i].num << endl ;
      }
    }
    

    min25筛

    ll k;
    ll check(ll v, ll n, ll ndr, ll nv) {
      return v >= ndr ? (n / v - 1) : (nv - v);
    }
    // ll S[10000000];
    // ll V[10000000];
    ll primenum(ll n) // O(n^(3/4))
    {
    ll r = (ll)sqrt(n);
    ll ndr = n / r;
    assert(r*r <= n && (r+1)*(r+1) > n);
    ll nv = r + ndr - 1;
    std::vector<ll> S(nv+1);
    std::vector<ll> V(nv+1);
    for(ll i=0;i<r;i++) 
      V[i] = n / (i+1);
    for(ll i=r;i<nv;i++) 
      V[i] = V[i-1] - 1;
    for(ll i = 0;i<nv;i++) 
      S[i] = V[i] - 1; //求素数个数
    for(ll p=2;p<=r;p++) {
      if(S[nv-p] > S[nv-p+1]) {
        ll sp = S[nv-p+1]; // sum of primes smaller than p
        ll p2 = p*p;
        for(ll i=0;i<nv;i++) {
          if(V[i] >= p2) {
            S[i] -= 1LL  * (S[check(V[i] / p, n, ndr, nv)] - sp);// //求素数个数
          }
          else break;
        }
      }
    }
    return S[0];
    }
    ll primesum(ll n) // O(n^(3/4)){
    ll r = (ll)sqrt(n);
    ll ndr = n / r;
    assert(r*r <= n && (r+1)*(r+1) > n);
    ll nv = r + ndr - 1;
    std::vector<ll> S(nv+1);
    std::vector<ll> V(nv+1);
    for(ll i=0;i<r;i++) 
      V[i] = n / (i+1);
    for(ll i=r;i<nv;i++) 
      V[i] = V[i-1] - 1;
    for(ll i = 0;i<nv;i++) 
      S[i] = V[i] * ( V[i] + 1) / 2 - 1; //求素数和
    for(ll p=2;p<=r;p++) { // p is prime
      if(S[nv-p] > S[nv-p+1]) {
        ll sp = S[nv-p+1]; // sum of primes smaller than p
        ll p2 = p*p;
        for(ll i=0;i<nv;i++) 
          if(V[i] >= p2) 
            S[i] -= p* (S[check(V[i] / p, n, ndr, nv)] - sp); //求素数和
          else break;
      }
    }
    return S[0];
    }
    

    ST表

    void solve(){
    for (int i=1;i<=n;i++) a[i][0]=r[i];
       for (int l=1;(1<<l)<=n;l++)
           for (int i=1;i+(1<<l)-1<=n;i++)
               a[i][l]=min(a[i][l-1],a[i+(1<<(l-1))][l-1]);
    }
    int get(int x , int y){
    int l=(int)log2(y-x+1);
    return min(a[x][l],a[y-(1<<l)+1][l]);
    }
    

    二次剩余

    const int mod=998244353;
    template<typename T>
    inline int pow(int x,T y)
    {
      rg int res=1;x%=mod;
      for(;y;y>>=1,x=(ll)x*x%mod)if(y&1)res=(ll)res*x%mod;
      return res;
    }
    inline int Quadratic_residue(const int a)
    {
      if(a==0)return 0;
      int b=(rand()<<14^rand())%mod;
      while(pow(b,(mod-1)>>1)!=mod-1)b=(rand()<<14^rand())%mod;
      int s=mod-1,t=0,x,inv=pow(a,mod-2),f=1;
      while(!(s&1))s>>=1,t++,f<<=1;
      t--,x=pow(a,(s+1)>>1),f>>=1;
      while(t)
      {
      	f>>=1;
      	if(pow((int)((ll)inv*x%mod*x%mod),f)!=1)x=(ll)x*pow(b,s)%mod;
      	t--,s<<=1;
      }
      return min(x,mod-x);
    }
    

    高精度

    vector<int> add(vector<int> A,vector<int> B){
      vector<int> C;
      int t=0;
      for(int i=0;i<A.size()||i<B.size();i++) {
          if(i<A.size()) t+=A[i];
          if(i<B.size()) t+=B[i];
          C.push_back(t%10);
          t/=10;
      }
      if(t)
          C.push_back(1);
      return C;
    }
    vector<int> muti(vector<int> A,int b){
      vector<int> C;
      int t=0;
      for(int i=0;i<A.size()||t;i++){
          if(i<A.size()) t+=A[i]*b;
          C.push_back(t%10);
          t/=10;
      }
      return C;
    }
    bool cmp(vector<int> A,vector<int> B){
      if(A.size()!=B.size())  return A.size()>B.size();
      for(int i=A.size();i>=0;i--)
          if(A[i]!=B[i])
              return A[i]>B[i];
      return true;
    }
    vector<int> div(vector<int> A, int b, int &r){
      vector<int> C;
      r = 0;
      for (int i = A.size() - 1; i >= 0; i -- ) {
          r = r * 10 + A[i];
          C.push_back(r / b);
          r %= b;
      }
      reverse(C.begin(), C.end());
      while (C.size() > 1 && C.back() == 0) C.pop_back();
      return C;
    }
    vector<int> sub(vector<int> A,vector<int> B){
      vector<int> C;
      int t=0;
      for(int i=0;i<A.size();i++){
          t=A[i]-t;
          if(i<B.size()) t-=B[i];
          C.push_back((t+10)%10);
          if(t<0) t=1;
          else t=0;
      }
      while(C.size()>1&&C.back()==0) C.pop_back();
      return C;
    }
    void print(vector<int> a){
    for(int i = a.size() - 1; i >= 0 ;i --) cout << a[i]  ;
    puts("") ;
    return ;
    }
    

    高精度组合数学

    void get_prime(int n){
      for(int i = 2;i <= n;i ++) {
           if(!st[i]) prime[tot ++] = i ;
           for(int j = 0; j < tot && i * prime[j] <= n ;j ++){
                st[i * prime[j]] = true ;
                if(i % prime[j] == 0) break ;
            }
       }
       return ;
    }
    int get(int a , int p){
      int sum = 0 ;
      while(a){
           sum += a / p ;
           a /= p ;
      }
      return sum ;
    }
    vector<int> muti(vector<int> a , int b){
      int t = 0 ;
      vector<int> res ;
      for(int i = 0;i < a.size() ;i ++){
          t += a[i] * b ;
          res.push_back(t % 10) ;
          t /= 10 ;
      }
      while(t){
          res.push_back(t % 10) ;
          t /= 10 ;
      }
      return res ;
    }
    int main(){
      int a , b ;
      cin >> a >>  b ;
      get_prime(a);
      for(int i = 0; i < tot ;i ++){
           int p = prime[i] ;
           sum[i] = get(a,p) - get(b,p) - get(a-b , p);
       }
       vector<int> res; 
       res.push_back(1);
       for(int i = 0;i < tot ; i ++)
        for(int j = 0; j < sum[i] ;j ++)
         res = muti(res, prime[i]);
       for(int i = res.size() - 1;i >= 0;i --)
        cout << res[i]  ;
       cout << endl ;
      return 0;
    }
    

    高斯消元

    #define D double
    D a[maxn][maxn];
    int n;
    int main(){
      scanf("%d",&n);
      for(re int i=1;i<=n;++i){
      	for(re int j=1;j<=n+1;++j){
      		scanf("%lf",&a[i][j]);
      	}
      }
      for(re int i=1;i<=n;++i)//枚举列(项){
      	re int max=i;
      	for(re int j=i+1;j<=n;++j)//选出该列最大系数
      		if(fabs(a[j][i])>fabs(a[max][i]))
              //fabs是取浮点数的绝对值的函数
      			max=j;
      	for(re int j=1;j<=n+1;++j)//交换
      		swap(a[i][j],a[max][j]);
      	if(!a[i][i])//最大值等于0则说明该列都为0,肯定无解{
      		puts("No Solution");
      		return 0;
      	}
      	for(re int j=1;j<=n;++j)//每一项都减去一个数(就是小学加减消元){
      		if(j!=i){
      			re D temp=a[j][i]/a[i][i];
      			for(re int k=i+1;k<=n+1;++k){
      				a[j][k]-=a[i][k]*temp;
                      //a[j][k]-=a[j][i]*a[i][k]/a[i][i];
      			}
      		}
      	}
      }
      //上述操作结束后,矩阵会变成这样
      /*
      k1*a=e1
      k2*b=e2
      k3*c=e3
      k4*d=e4
      */
      //所以输出的结果要记得除以该项系数,消去常数
      for(re int i=1;i<=n;++i)
      {
      	printf("%.2lf
    ",a[i][n+1]/a[i][i]);
      }
      return 0;
    }
    

    康托展开

    #include <bits/stdc++.h>
    using namespace std ;
    //返回数组a中当下顺序的康拖映射
    typedef unsigned long long ll ;
    ll b[30] ;
    //对前 10 个自然数(0 ~ 9)的阶乘存入表
    //以免去对其额外的计算
    ll fact[22] ;
    /**
    * @brief 康拓展开
    *
    * @param[in] permutation 输入的一个全排列
    * @param[out] num 输入的康拓映射,即是第几个全排列
    */
    ll contor(vector<ll> permutation) {
      ll num = 0;
      ll len = permutation.size();
      for (ll i = 0; i < len; ++i) {
          ll cnt = 0; // 在 i 之后,比 i 还小的有几个
          for (ll j = i + 1; j < len; ++j)
              if (permutation[i] > permutation[j]) ++cnt;
          num += cnt * fact[len - i - 1];
      }
      return num + 1;
    }
    //对前 10 个自然数(0 ~ 9)的阶乘存入表
    //以免去对其额外的计算
    /**
    * @brief 逆康拓展开
    *
    * @param[in] bits 给定全排列的使用数字个数
    * @param[in] num 给定全排列的次位
    * @param[out] permutation 输出对应的全排列
    */
    vector<ll> revContor(ll bits, ll num) {
      num = num - 1; //有 num - 1 个排列比目标序列要小
      vector<bool> vis(bits + 1, false);
      vector<ll> permutation(bits, -1);
    
      ll n, residue = num;
      for (ll i = 0; i < bits; ++i) {
          n = residue / (fact[bits - i - 1]);
          residue = residue % (fact[bits - i - 1]);
    
          for (ll j = 1; j <= bits; ++j) {
              if (!vis[j] && !(n--)) {
                  vis[j] = true;
                  permutation[i] = j;
                  break;
              }
          }
      }
      return permutation;
    }
    int main()
    {
    int n , m ;
    cin >> n >> m ;
    fact[0] = 1;
    for(ll i = 1; i <= n ;i ++)
     fact[i] = fact[i - 1] * i ;
    for(int i = 1; i <= m ;i ++)
     {
       char s[2] ;
       cin >> s ;
       if(s[0] == 'P') // 求第x大的字典序
        {
          ll x ;cin >> x ;
          vector<ll> t = revContor(n , x) ;
          for(auto xx : t)
           cout << xx << " " ;
          puts("") ;
        }
        else      // 求字典序第几大
         {
           vector<ll> b ;
           for(ll i = 1 , x ; i <= n ;i ++) cin >> x , b.push_back(x) ;
           cout << contor(b) << endl ;
         }
     }
    return 0 ;
    }
    

    扩展欧几里得

    ll exgcd(ll a , ll b , ll &x , ll &y){
      if(b == 0){
           x = 1 , y = 0 ;
           return a ;
       }
       ll d = exgcd(b , a % b , y , x ) ;
       y -= (a / b) * x ;
       return d ;
    }
    

    扩展欧几里得求正整数

    ll exgcd(ll a,ll b,ll &x,ll &y){
      if(b==0){
      	x=1;
      	y=0;
      	return a;
      }
      ll q=exgcd(b,a%b,x,y);
      ll t=x;
      x=y;
      y=t-a/b*y;
      return q;
    }
    ll gcd(ll a , ll b)
    {
    return b == 0 ? a : gcd(b , a % b) ;
    }
    ll tx , ty , tz ;
    // 求正整数
    void get(ll a , ll b , ll d)
    {
      ll x , y ;
      int md=exgcd(a,b,x,y);
      a/=md,b/=md,d/=md;
      int x1=x*d;
    x1=(x1%b+b)%b;
    ll y1=(d-a*x1)/b;
    y1=abs(y1);
    ll y2=y*d;
    y2=(y2%a+a)%a;
    int x2=(d-b*y2)/a;
    x2=abs(x2);
    if(x1+y1<x2+y2) tx = x1 , ty = y1 ;
    else tx = x2 , ty = y2 ;
    }
    

    三维差分

    for(int i = 1; i <= m ;i ++ ) {
    int a , b , c , d , e , f ;
    // a <= x <= d , b <= y <= e , c <= z <= f
    cin >> a >> b >> c >> d >> e >> f ;
    sum[a][b][c] ++ ;
    sum[a][e + 1][c] -- ;
    sum[d + 1][b][c] -- ;
    sum[a][b][f + 1] -- ;
    sum[d + 1][b][f + 1] ++ ;
    sum[a][e + 1][f + 1] ++ ;
    sum[d + 1][e + 1][c] ++ ;
    sum[d + 1][e + 1][f + 1] -- ;
    }
    for(int i = 1; i <= n ;i ++ ) {
    for(int j = 1; j <= n ;j ++ ) {
      for(int k = 1; k <= n ;k ++ ) {
        sum[i][j][k] += sum[i - 1][j][k] + sum[i][j - 1][k] + sum[i][j][k - 1] + sum[i - 1][j - 1][k - 1] - sum[i - 1][j - 1][k] - sum[i - 1][j][k - 1] - sum[i][j - 1][k - 1] ;
      }
    }
    }
    

    几何

    直线相交求交点

    const int maxh=62;
    #define rint register int
    int cmp(double x){
       if(fabs(x)<eps) return 0;
       if(x>0) return 1;
       return -1;
    }
    double sqr(double x){
       return x*x;
    }
    struct point {
       double x,y;
       point(){}
       point(double a,double b):x(a),y(b){}
       void input(){
           cin>>x>>y;
       }
       friend point operator -(const point &a,const point &b){
           return point (a.x-b.x,a.y-b.y);
       }
       friend point operator *(const point &a,const double &b){
           return point (a.x*b,a.y*b);
       }
       friend point operator *(const double &b,const point &a){
           return point (a.x*b,a.y*b);
       }
       friend point operator /(const point &a,const double &b){
           return point (a.x/b,a.y/b);
       }
    };
    double cross(const point &a,const point &b){
       return a.x*b.y-a.y*b.x;
    }
    struct line {
       point a,b;
       line(){}
       line(point x,point y):a(x),b(y){}
       void input(){
           a.input();
           b.input();
       }
    }a[N];
    bool parallel(line a,line b){
       return !cmp(cross(a.a-a.b,b.a-b.b));
    }
    bool line_make_point(line a,line b,point &res){
       if(parallel(a,b)) return false;
       double s1=cross(a.a-b.a,b.b-b.a);
       double s2=cross(a.b-b.a,b.b-b.a);
       res=(s1*a.b-s2*a.a)/(s1-s2);
       return true;
    }
    double res=0;
    int main() {
       cin>>n;
       for(int i=1;i<=n;i++){
           a[i].input();
       }
       for(int i=1;i<=n;i++)
           for(int j=1;j<=n;j++)
               for(int k=1;k<=n;k++){
                   point x,y,z;
                   if(!line_make_point(a[i],a[j],x)) continue;
                   if(!line_make_point(a[i],a[k],y)) continue;
                   if(!line_make_point(a[j],a[k],z)) continue;
                   double b,c,d;
                   b=sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
                   c=sqrt(sqr(x.x-z.x)+sqr(x.y-z.y));
                   d=sqrt(sqr(y.x-z.x)+sqr(y.y-z.y));
                   double p=(b+c+d);
                   res=max(res,p/*sqrt(p*(p-b)*(p-c)*(p-d))*/);
               }
       if(abs(res)<eps) {
           cout<<"no triangle";
       } else cout<<fixed<<setprecision(12)<<res;
       return 0;
    }
    

    圆与正方形的关系

    struct node
    {
     int x , y , r ;
    }p[6] , z[6];
    int check1(node a){
     if(a.x > z[0].x && a.x < z[1].x && a.y > z[0].y && a.y < z[3].y) return 1 ;
     for(int i = 0 ;i < 4 ;i ++){
        if(z[i].x == z[i + 1].x)
           if(a.x == z[i].x && a.y >= min(z[i].y , z[i + 1].y) && a.y <= max(z[i].y , z[i + 1].y)) return 2 ;
        if(z[i].y == z[i + 1].y)
           if(a.y == z[i].y && a.x >= min(z[i].x , z[i + 1].x) && a.x <= max(z[i].x , z[i + 1].x)) return 2 ;
      }
     return 0 ;
    }
    int check2(node a){
     int t = (p[0].x - a.x) * (p[0].x - a.x) + (p[0].y - a.y) * (p[0].y - a.y) ;
     if(t == p[0].r * p[0].r) return 2 ;
     if(t < p[0].r * p[0].r) return 1 ;
     return 0 ;
    }
    bool have(){
     for(int i = 0 ;i <= 4 ;i ++)
      if(check1(p[i]) == 1)  return 1 ;
     for(int i = 0 ;i < 4;i ++)
      if(check2(z[i]) == 1)  return 1 ;
     return 0 ;
    }
    bool touch(){
     for(int i = 0 ;i <= 4 ;i ++)
      if(check1(p[i]) == 2) return 1 ;
     for(int i = 0 ;i < 4 ;i ++)
      if(check2(z[i]) == 2) return 1 ;
     return 0 ;
    }
    int main(){
     int r ;
     cin >> p[0].x >> p[0].y >> p[0].r ;
     p[1].x = p[0].x + p[0].r , p[1].y = p[0].y ;
     p[2].x = p[0].x , p[2].y = p[0].y + p[0].r ;
     p[3].x = p[0].x - p[0].r , p[3].y = p[0].y ;
     p[4].x = p[0].x , p[4].y = p[0].y - p[0].r ;
     cin >> z[0].x >> z[0].y >> r;
     z[1].x = z[0].x + r , z[1].y = z[0].y ;
     z[2].x = z[0].x + r , z[2].y = z[0].y + r ;
     z[3].x = z[0].x , z[3].y = z[0].y + r ;
     z[4] = z[0] ;
     if(have()) puts("2") ;
     else if(touch()) puts("1") ;
     else puts("0") ;
     return 0 ;
    }
    

    圆与矩形相交面积

    struct Point{    double x, y;
       Point() {}
       Point(double xx, double yy)
       {
           x = xx;
           y = yy;
       }
       Point operator-(Point s) { return Point(x - s.x, y - s.y); }
       Point operator+(Point s) { return Point(x + s.x, y + s.y); }
       double operator*(Point s) { return x * s.x + y * s.y; }
       double operator^(Point s) { return x * s.y - y * s.x; }
    } p[M];
    double max(double a, double b) { return a > b ? a : b; }
    double min(double a, double b) { return a < b ? a : b; }
    double len(Point a) { return sqrt(a * a); }  // ���� a �ij���
    double dis(Point a, Point b) { return len(b - a); } // a �� b ֮���ľ���
    double cross(Point a, Point b, Point c)             // (a , b) ��(a , c){
       return (b - a) ^ (c - a);
    }
    double dot(Point a, Point b, Point c) // (a , b) * (a , c){
       return (b - a) * (c - a);
    }
    int judge(Point a, Point b, Point c) // �ж� ��c �Ƿ��� �߶� ab ��{
       if (c.x >= min(a.x, b.x) && c.x <= max(a.x, b.x) && c.y >= min(a.y, b.y) && c.y <= max(a.y, b.y))
           return 1;
       return 0;
    }
    double area(Point b, Point c, double r) // Բ�� a �� �뾶 r �� �������� a , b , c �ཻ������{
       Point a(0.0, 0.0);
       if (dis(b, c) < eps)
           return 0.0;
       double h = fabs(cross(a, b, c)) / dis(b, c);
       if (dis(a, b) > r - eps && dis(a, c) > r - eps){
           double angle = acos(dot(a, b, c) / dis(a, b) / dis(a, c));
           if (h > r - eps)
               return 0.5 * r * r * angle;
           else if (dot(b, a, c) > 0 && dot(c, a, b) > 0){
               double angle1 = 2 * acos(h / r);
               return 0.5 * r * r * fabs(angle - angle1) + 0.5 * r * r * sin(angle1);
           }
           else return 0.5 * r * r * angle;
       }
       else if (dis(a, b) < r + eps && dis(a, c) < r + eps) return 0.5 * fabs(cross(a, b, c));
       else{
           if (dis(a, b) > dis(a, c))swap(b, c);
           if (fabs(dis(a, b)) < eps) return 0.0;
           if (dot(b, a, c) < eps) {
               double angle1 = acos(h / dis(a, b));
               double angle2 = acos(h / r) - angle1;
               double angle3 = acos(h / dis(a, c)) - acos(h / r);
               return 0.5 * dis(a, b) * r * sin(angle2) + 0.5 * r * r * angle3;
           }
           else{
               double angle1 = acos(h / dis(a, b));
               double angle2 = acos(h / r);
               double angle3 = acos(h / dis(a, c)) - angle2;
               return 0.5 * r * dis(a, b) * sin(angle1 + angle2) + 0.5 * r * r * angle3;
           }
       }
    }
    int main(){
       int n = 4;
       double rx, ry, R;
       scanf("%lf%lf%lf", &rx, &ry, &R);
       scanf("%lf%lf%lf%lf", &p[1].x, &p[1].y, &p[3].x, &p[3].y);
       p[2].x = p[1].x;
       p[2].y = p[3].y;
       p[4].x = p[3].x;
       p[4].y = p[1].y;
       p[5] = p[1];
       Point O(rx, ry);
       for (int i = 1; i <= n + 1; i++)
           p[i] = p[i] - O;
       O = Point(0, 0);
       double sum = 0;
       for (int i = 1; i <= n; i++){
           int j = i + 1;
           double s = area(p[i], p[j], R);
           if (cross(O, p[i], p[j]) > 0) sum += s;
           else sum -= s;
       }
       printf("%.4lf
    ", fabs(sum));
    }
    

    一个点是否在线段上

    double direction( node p1,node p2,node p ){
       return ( p1.x -p.x )*( p2.y-p.y) -  ( p2.x -p.x )*( p1.y-p.y)   ;
    }
    int on_segment( node p1,node p2 ,node p ){
       double max=p1.x > p2.x ? p1.x : p2.x ;
       double min =p1.x < p2.x ? p1.x : p2.x ;
    double max1=p1.y > p2.y ? p1.y : p2.y ;
       double min1=p1.y < p2.y ? p1.y : p2.y ;
       if( p.x >=min && p.x <=max &&
     p.y >=min1 && p.y <=max1 )
           return 1;
       else
           return 0;
    }
    bool check1(node p1 , node p2 , node q){
     if( !on_segment( p1,p2,q ) ) return 0 ;
     if( direction( q,p1,p2 )==0 )return 1 ;
     else return 0 ;
    }
    bool check(int x ,int y){
     for(int i = 1; i <= n ;i ++)
      if(x != i && y != i && check1(a[x] , a[y] , a[i])) return 0 ;
     for(int i = 1; i <= n ;i ++)
        if(check1(a[x] , a[y] , b[i]))
         return 0 ;
     return 1 ;
    }
    

    计算几何板子

    // 直线相交求交点 , 凸包算法, 求点是否在多边形范围内
    struct point {
       double x,y;
       point(){}
       point(double a,double b):x(a),y(b){}
       void input(){
           cin>>x>>y;
       }
       friend point operator -(const point &a,const point &b){
           return point (a.x-b.x,a.y-b.y);
       }
       friend point operator *(const point &a,const double &b){
           return point (a.x*b,a.y*b);
       }
       friend point operator *(const double &b,const point &a){
           return point (a.x*b,a.y*b);
       }
       friend point operator /(const point &a,const double &b){
           return point (a.x/b,a.y/b);
       }
    }p[N] , s[N] ;
    struct line {
       point a,b;
       line(){}
       line(point x,point y):a(x),b(y){}
       void input(){
           a.input();
           b.input();
       }
    }a[N];
    int cmp(double x){
       if(fabs(x)<eps) return 0;
       if(x>0) return 1;
       return -1;
    }
    double sqr(double x){
       return x*x;
    }
    double cross(const point &a,const point &b){
       return a.x*b.y-a.y*b.x;
    }
    bool parallel(line a,line b){
       return !cmp(cross(a.a-a.b,b.a-b.b));
    }
    bool line_make_point(line a,line b,point &res){
       if(parallel(a,b)) return false;
       double s1=cross(a.a-b.a,b.b-b.a);
       double s2=cross(a.b-b.a,b.b-b.a);
       res=(s1*a.b-s2*a.a)/(s1-s2);
       return true;
    }
    double dis(point a,point b)
    {
       point c=a-b;
       return sqrt(c.x*c.x+c.y*c.y);
    }
    double cross(point &a,point &b)//叉乘{
       return a.x*b.y-a.y*b.x;
    }
    bool cmp(point a,point b)//极角排序{
       double x=cross(a-p[1],b-p[1]);//以p[1]为积点
    
       if(x>0) return 1;//让a处于b的顺时针
       if(x==0&&dis(a,p[1])<dis(b,p[1])) return 1;//角度相同按距离排序
       return 0;
    }
    //
    double mulx(point p1,point p2,point p3){
       return cross(p2-p1,p3-p1);
    }
    double area(point *p,int n){
       if(n<3) return 0;
       p[n+1]=p[1];
       double sum=0;
       for(int i=1;i<=n;i++)
       {
           sum+=p[i].x*p[i+1].y-p[i].y*p[i+1].x;//不管这个多边形在哪,都以原点为分割点,就算原点在外面也可以算出,因为有正负可以抵消掉多余的
       }
       sum=fabs(sum/2.0);
       return sum;
    }
    // 判断点是否在这个多边形范围内
    bool intubao(point *s,int n,point a){
       int f=0;
       for(int i=0;i<n;i++)
       {
           if(cross(s[(i+1)%n]-s[i],a-s[i])>0) f++;
       }
       //if(cross(s[1]-s[n],a-s[n])>0) f++;
       if(f==n) return 1;
       return 0;
    }
    // 先按照x , 然后y
    bool cmp2(point a,point b){
       if (a.x!=b.x)
           return a.x<b.x;
       return a.y<b.y;
    }
    int n,q , m ;
    void Tubao() {
     sort(p,p+n,cmp2);
     for(int i=0;i<n;i++){
         while(m>1&&mulx(s[m-2],s[m-1],p[i])<=0) m--;
         s[m++]=p[i];
     }
     int kk=m;
     for(int i=n-2;i>=0;i--){
         while(m>kk&&mulx(s[m-2],s[m-1],p[i])<=0) m--;
         s[m++]=p[i];
     }
     if(n>1)
         m--;
    }
    

    DP

    求所有区间内任意个数相加和为k的总方案数

    /*
    题意:一个长度为n的数组,求任意[ L,R ]区间和为S的总数。
    题解:01背包
    dp[N]代表前i个数中和为N的数量
    每次dp[0]+=1,我们01背包板子循环到第i个物品求的是前i个物品和为N的数量,并没有
    [2,i]、[3,i]、[4,i]、…、[i,i]这些情况,少了i-1种情况答案肯定不对,所以我们只需要把dp[0]赋值为 1 + (i-1) 原始就为1,再加上少的i-1中情况。
    */
    ll a[maxn],dp[maxn];
    int main(){
      ll n,k;
      cin>>n>>k;
      for(int i=1;i<=n;i++) cin>>a[i];
      ll ans=0;
      for(int i=1;i<=n;i++){
      	dp[0]+=1;//[2,i]、[3,i]、[4,i]、...、[i,i]
      	for(int j=k;j>=a[i];j--)
      		dp[j]=(dp[j]+dp[j-a[i]])%mod;
      	ans=(ans+dp[k])%mod;//前i个数的和为K的数量
      }
      cout<<ans<<endl;
      return 0;
    }
    

    BM线性递推

    #define pb push_back
    #define mp make_pair
    #define all(x) (x).begin(),(x).end()
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const ll mod=1000000007;
    ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    ll n;
    namespace linear_seq {
      const int N=10010;
      ll res[N],base[N],_c[N],_md[N];
      vector<int> Md;
      void mul(ll *a,ll *b,int k) {
          rep(i,0,k+k) _c[i]=0;
          rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
          for (int i=k+k-1;i>=k;i--) if (_c[i])
              rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
          rep(i,0,k) a[i]=_c[i];
      }
      int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+...
          ll ans=0,pnt=0;
          int k=SZ(a);
          assert(SZ(a)==SZ(b));
          rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
          Md.clear();
          rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
          rep(i,0,k) res[i]=base[i]=0;
          res[0]=1;
          while ((1ll<<pnt)<=n) pnt++;
          for (int p=pnt;p>=0;p--) {
              mul(res,res,k);
              if ((n>>p)&1) {
                  for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                  rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
              }
          }
          rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
          if (ans<0) ans+=mod;
          return ans;
      }
      VI BM(VI s) {
          VI C(1,1),B(1,1);
          int L=0,m=1,b=1;
          rep(n,0,SZ(s)) {
              ll d=0;
              rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
              if (d==0) ++m;
              else if (2*L<=n) {
                  VI T=C;
                  ll c=mod-d*powmod(b,mod-2)%mod;
                  while (SZ(C)<SZ(B)+m) C.pb(0);
                  rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                  L=n+1-L; B=T; b=d; m=1;
              } else {
                  ll c=mod-d*powmod(b,mod-2)%mod;
                  while (SZ(C)<SZ(B)+m) C.pb(0);
                  rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                  ++m;
              }
          }
          return C;
      }
      int gao(VI a,ll n) {
          VI c=BM(a);
          c.erase(c.begin());
          rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
          return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
      }
    };
    int main() {
      /*push_back 进去前 8~10 项左右、最后调用 gao 得第 n 项*/
      vector<int>v;
      v.push_back(3);
      v.push_back(9);
      v.push_back(20);
      v.push_back(46);
      v.push_back(106);
      v.push_back(244);
      v.push_back(560);
      v.push_back(1286);
      v.push_back(2956);
      v.push_back(6794);
      int nCase;
      scanf("%d", &nCase);
      while(nCase--){
          scanf("%lld", &n);
          printf("%lld
    ",1LL * linear_seq::gao(v,n-1) % mod);
      }
    }
    

    搜索

    A_star算法

    第 K 短路
    int h[N] , rh[N] , e[N] , ne[N] , w[N] , idx;
    void add(int *h , int a , int b , int c){
       e[idx] = b , ne[idx] = h[a] , w[idx] = c , h[a] = idx ++ ;
    }
    int S , T , K ;
    int dis[N] , f[N] , n , m , st[N] ;
    void dij(){
       priority_queue<PII , vector<PII> , greater<PII> > q ;
       memset(dis , 0x3f , sizeof dis) ;
       dis[T] = 0 ;
       memset(st , 0 , sizeof st) ;
       q.push({0 , T}) ;
       while(q.size()){
            auto t = q.top() ; q.pop() ;
            int u = t.second ;
            if(st[u]) continue ;
            st[u] = 1 ;
            for(int i = rh[u] ; ~i ; i = ne[i]){
                 int v = e[i] ;
                 if(dis[v] > dis[u] + w[i])
                  dis[v] = dis[u] + w[i] , q.push({dis[v] , v}) ;
             }
        }
        memcpy(f , dis , sizeof f) ;
        return ;
    }
    int a_star(){
       priority_queue<PIII , vector<PIII> , greater<PIII > > q ;
       q.push({f[S] , {0 , S}}) ;
       memset(st , 0 , sizeof st) ;
       while(q.size()){
            auto t = q.top() ; q.pop() ;
            int u = t.second.second , distance = t.second.first ;
            if(st[u] >= K) continue ;
            st[u] ++ ;
            if(u == T && st[u] == K) return distance ;
            for(int i = h[u] ; ~i ; i = ne[i]){
                 int v = e[i] ;
                 if(st[v] < K)
                  q.push({distance + w[i] + f[v] , {distance + w[i] , v}}) ;
             }
        }
        return -1 ;
    }
    int main(){
     n = in() , m = in() ;
     memset(h , -1 , sizeof h ) , memset(rh , -1 , sizeof rh) ;
     for(int i = 1 , a , b , c ; i <= m ;i ++)
          a = in() , b = in() , c = in() , add(h , a , b , c) , add(h , b , a , c) , add(rh , b , a , c) , add(rh , a , b , c) ;
     S = 0 , T = n - 1 , K = 2 ;
     if(S == T) K ++ ;
     dij() ;
     memset(st , 0 , sizeof st) ;
     int t = a_star() ;
     cout << t << endl ;
     return 0 ;
    }
    /*
    */
    
  • 相关阅读:
    强迫症患者
    GG的匹配串
    漂洋过海来看你
    Fire or Retreat
    1011. A+B和C (15)
    1010. 一元多项式求导 (25)
    1009. 说反话 (20)
    1008. 数组元素循环右移问题 (20)
    1007. 素数对猜想 (20)
    1006. 换个格式输出整数 (15)
  • 原文地址:https://www.cnblogs.com/spnooyseed/p/13863378.html
Copyright © 2011-2022 走看看