acm常用模板

ACM MM 2025 Overleaf 模板指导 本文介绍了 ACM 文章的排版模板 “acmart” 的使用方法,涵盖了文档结构、格式化规则、数学公式、图像、表格、引用格式以及多语言支持等内容。文章详细说明了如何正确使用 $\LaTeX$ 进行章节划分、插入图表、编写数学公式,并遵循 ACM 期刊和会议论文的格式要求。此外,还包括了 SIGCHI 扩展摘要的特殊格式、附录的使用、致谢部分的编写,以及参考文献的管理方式。本文提供了具体的代码示例和最佳实践,以帮助作者高效地编写符合 ACM 规范的论文。 阅读详情

数位dp

moban
//    pos    = 当前处理的位置(一般从高位到低位)
//    pre    = 上一个位的数字(更高的那一位)
//    status = 要达到的状态,如果为1则可以认为找到了答案,到时候用来返回,
//            给计数器+1。
//    limit  = 是否受限,也即当前处理这位能否随便取值。如567,当前处理6这位,
//            如果前面取的是4,则当前这位可以取0-9。如果前面取的5,那么当前
//            这位就不能随便取,不然会超出这个数的范围,所以如果前面取5的
//            话此时的limit=1,也就是说当前只可以取0-6。
//
//    用DP数组保存这三个状态是因为往后转移的时候会遇到很多重复的情况。
int    dfs(int pos,int pre,int status,int limit)
{
    //已结搜到尽头,返回"是否找到了答案"这个状态。
    if(pos < 1)
        return    status;

    //DP里保存的是完整的,也即不受限的答案,所以如果满足的话,可以直接返回。
    if(!limit && DP[pos][pre][status] != -1)
        return    DP[pos][pre][status];

    int    end = limit ? DIG[pos] : 9;
    int    ret = 0;

    //往下搜的状态表示的很巧妙,status用||是因为如果前面找到了答案那么后面
    //还有没有答案都无所谓了。而limti用&&是因为只有前面受限、当前受限才能
    //推出下一步也受限,比如567,如果是46X的情况,虽然6已经到尽头,但是后面的
    //个位仍然可以随便取,因为百位没受限,所以如果个位要受限,那么前面必须是56。
    //
    //这里用"不要49"一题来做例子。
    for(int i = 0;i <= end;i ++)
        ret += dfs(pos - 1,i,status || (pre == 4 && i == 9),limit && (i == end));

    //DP里保存完整的、取到尽头的数据
    if(!limit)
        DP[pos][pre][status] = ret;

    return    ret;
}
HDU 5787 K-wolf Number(数位dp)
/*
- 求1≤L≤R≤1018范围内,每2≤K≤5个数字都不同的数字有多少

- f[i][pre][5]:=从高到低,填到第i位,且之前的数字是pre,pre已经有1∼4位数合法数字数
- 由于不能有前导0,所以开个first判一判,其他套个板子就好了
*/
const int N = 1e4 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
LL l, r, f[20][N][5];
int k;

const int ten[] = {1, 10, 100, 1000, 10000, 100000};
int get(int x, int i) {
    return x / ten[i] % 10;
}

int add(int x, int y) {
    return (x * 10 + y) % ten[k - 1];
}

int digit[20];

LL dfs(int i, int pre, int num, bool first, bool e) {
    if(!i) return 1;
    if(!e && ~f[i][pre][num]) return f[i][pre][num];
    LL ret = 0;
    int to = e ? digit[i] : 9;
    for(int d = 0; d <= to; ++d) {
        bool ok = true;
        for(int j = 0; j < min(k - 1, num) && ok; ++j)
            if(get(pre, j) == d) ok = false;
        if(!ok) continue;
        ret += dfs(i - 1, first && !d ? 0 : add(pre, d),
                   first && !d ? 0 : min(k - 1, num + 1),
                   first && !d, e && d == to);
    }
    return e ? ret : f[i][pre][num] = ret;
}

LL calc(LL x) {
    int cnt = 0;
    for(; x; x /= 10) digit[++cnt] = x % 10;
    return dfs(cnt, 0, 0, 1, 1);
}

bool judge(int x, int k) {
    int cnt = 0;
    for(; x; x /= 10) digit[++cnt] = x % 10;

    for(int i = 1; i <= cnt; ++i) {
        bool ok = true;
        for(int j = 1; j < k; ++j) {
            if(i - j >= 1 && digit[i] == digit[i - j]) {
                ok = false;
                break;
            }
        }
        if(!ok) return false;
    }
    return true;
}
int main() {
    ios_base::sync_with_stdio(0);

    while(scanf("%I64d%I64d%d", &l, &r, &k) == 3) {
        memset(f, -1, sizeof f);
        LL ans = calc(r) - calc(l - 1);
        printf("%I64d\n", ans);
    }

    return 0;
}
二维bit优化dp
/*
cf355 (Div. 2) D. Vanya and Treasure(dp、二维BIT优化)
题意:N,M≤300,P≤N×M,给定一个N×M图,每个格子Aij是1∼P的数字
从(1, 1)出发,两个格子的距离定义为曼哈顿距离,按顺序取1∼P的数字
问最短路是多少

优化一:
令cnt[i]:=i这个数字的个数,当cnt[i−1]×cnt[i]≤n×m的时候直接更新,否则bfs一波
这个时间复杂度是O(nmnm−−−√),如果有人懂详细证明请教我。。
优化二:
我们展开这个dp转移方程:
f[x2][y2]=min{ f[x1][y1]+abs(x1−x2)+abs(y1−y2) }
*/
const int N = 300 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m, p;
int s[N][N], f[N][N];
typedef pair<int, int> P;
vector<P> G[N * N];

struct BIT {
    int n, b[N][N];
    int timStp, vis[N][N];
    void init(int _n) {
        n = _n;
        timStp = 1;
        memset(vis, 0, sizeof vis);
    }
    void newOne() {
        ++timStp;
    }
    void update(int x, int y, int v) {
        for(int i = x; i <= n; i += i & -i) {
            for(int j = y; j <= n; j += j & -j) {
                if(vis[i][j] != timStp) vis[i][j] = timStp, b[i][j] = INF;
                b[i][j] = min(b[i][j], v);
            }
        }
    }
    int query(int x, int y) {
        int ret = INF;
        for(int i = x; i; i -= i & -i)
            for(int j = y; j; j -= j & -j)
                if(vis[i][j] == timStp) ret = min(ret, b[i][j]);
        return ret;
    }
} bit[4];

int main() {

    ios_base::sync_with_stdio(0);

    scanf("%d%d%d", &n, &m, &p);
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            scanf("%d", s[i] + j);
            G[s[i][j]].push_back({i, j});
        }
    }

    memset(f, 0x3f, sizeof f);
    int nm = max(n, m);
    for(int i = 0; i < 4; ++i) bit[i].init(nm);
    //UL - f[x2][y2] = min { f[x1][y1] - x1 - y1 + x2 + y2 }
    //UR - f[x2][y2] = min { f[x1][y1] + x1 - y1 - x2 + y2 }
    //BR - f[x2][y2] = min { f[x1][y1] + x1 + y1 - x2 - y2 }
    //BL - f[x2][y2] = min { f[x1][y1] - x1 + y1 + x2 - y2 }
    for(int i = 0; i < G[1].size(); ++i) {
        int x = G[1][i].first, y = G[1][i].second;
        f[x][y] = x + y - 2;
        bit[0].update(x, y, f[x][y] - x - y);
        bit[1].update(nm - x + 1, y, f[x][y] + x - y);
        bit[2].update(nm - x + 1, nm - y + 1, f[x][y] + x + y);
        bit[3].update(x, nm - y + 1, f[x][y] - x + y);
    }

    for(int i = 2; i <= p; ++i) {
        for(int j = 0; j < G[i].size(); ++j) {
            int x = G[i][j].first, y = G[i][j].second, val = INF;
            val = min(val, bit[0].query(x, y) + x + y);
            val = min(val, bit[1].query(nm - x + 1, y) - x + y);
            val = min(val, bit[2].query(nm - x + 1, nm - y + 1) - x - y);
            val = min(val, bit[3].query(x, nm - y + 1) + x - y);
            f[x][y] = val;
        }

        for(int j = 0; j < 4; ++j) bit[j].newOne();
        for(int j = 0; j < G[i].size(); ++j) {
            int x = G[i][j].first, y = G[i][j].second;
            bit[0].update(x, y, f[x][y] - x - y);
            bit[1].update(nm - x + 1, y, f[x][y] + x - y);
            bit[2].update(nm - x + 1, nm - y + 1, f[x][y] + x + y);
            bit[3].update(x, nm - y + 1, f[x][y] - x + y);
        }
    }

    int x = G[p][0].first, y = G[p][0].second;
    printf("%d\n", f[x][y]);

    return 0;
}


/*
ZOJ 3256
N*M(2<=N<=7,1<=M<=10^9)的方格,问从左上角的格子到左下角的格子,
而且仅经过所有格子一次的路径数
插头DP+矩阵加速

对于一个图的邻接矩阵的N次方,其中(i,j)位置上的元素表示
点i经过N步到达点j的方案数
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;

const int STATE=1010;
const int HASH=419;//这个小一点,效率高
const int MOD=7777777;

int N,M;
int D;
int code[10];
int ch[10];
int g[200][200];//状态转移图

struct Matrix
{
    int n,m;
    int mat[200][200];
};
Matrix mul(Matrix a,Matrix b)//矩阵相乘,要保证a的列数和b的行数相等
{
    Matrix ret;
    ret.n=a.n;
    ret.m=b.m;
    long long sum;
    for(int i=0;i<a.n;i++)
       for(int j=0;j<b.m;j++)
       {
           sum=0;
           for(int k=0;k<a.m;k++)
           {
               sum+=(long long)a.mat[i][k]*b.mat[k][j];
               //sum%=MOD;//加了这句话就会TLE,坑啊。。。
           }
           ret.mat[i][j]=sum%MOD;
       }
    return ret;
}
Matrix pow_M(Matrix a,int n)//方阵的n次方
{
    Matrix ret=a;
    memset(ret.mat,0,sizeof(ret.mat));
    for(int i=0;i<a.n;i++)ret.mat[i][i]=1;//单位阵
    Matrix temp=a;
    while(n)
    {
        if(n&1)ret=mul(ret,temp);
        temp=mul(temp,temp);
        n>>=1;
    }
    return ret;
}

struct HASHMAP
{
    int head[HASH],next[STATE],size;
    int state[STATE];
    void init()
    {
        size=0;
        memset(head,-1,sizeof(head));
    }
    int push(int st)
    {
        int i,h=st%HASH;
        for(i=head[h];i!=-1;i=next[i])
           if(state[i]==st)
              return i;
        state[size]=st;
        next[size]=head[h];
        head[h]=size++;
        return size-1;
    }
}hm;
void decode(int *code,int n,int st)
{
    for(int i=n-1;i>=0;i--)
    {
        code[i]=st&3;
        st>>=2;
    }
}
int encode(int *code,int n)
{
    int cnt=1;
    memset(ch,-1,sizeof(ch));
    ch[0]=0;
    int st=0;
    for(int i=0;i<n;i++)
    {
        if(ch[code[i]]==-1)ch[code[i]]=cnt++;
        code[i]=ch[code[i]];
        st<<=2;
        st|=code[i];
    }
    return st;
}

bool check(int st,int nst)//判断两种状态能不能转移
{
    decode(code,N,st);
    int flag=0;//标记格子上边是否有插头
    int cnt=0;
    int k;
    for(int i=0;i<N;i++)
    {
        if(flag==0)//这个格子上边没有插头
        {
            if(code[i]==0&&(nst&(1<<i))==0)//左边和右边都没有插头
               return false;
            if(code[i]&&(nst&(1<<i)))continue;
            if(code[i])flag=code[i];//插头从左边过来,从下边出去
            else flag=-1;//插头从下边进来从右边出去
            k=i;
        }
        else
        {
            if(code[i]&&(nst&(1<<i)))//左边和右边和上边都有插头
               return false;
            if(code[i]==0&&(nst&(1<<i))==0)continue;
            if(code[i])
            {
                if(code[i]==flag&&((nst!=0)||i!=N-1))return false;//只有最后一个格子才能合起来
                if(flag>0)
                {
                    for(int j=0;j<N;j++)
                      if(code[j]==code[i]&&j!=i)
                          code[j]=code[k];
                    code[i]=code[k]=0;
                }
                else
                {
                    code[k]=code[i];
                    code[i]=0;
                }
            }
            else
            {
                if(flag>0)code[i]=code[k],code[k]=0;
                else code[i]=code[k]=N+(cnt++);
            }
            flag=0;
        }
    }
    if(flag!=0)return false;
    return true;
}
struct Node
{
    int g[200][200];
    int D;
}node[20];//打表之用
void init()
{
    if(node[N].D!=0)
    {
        memcpy(g,node[N].g,sizeof(node[N].g));
        D=node[N].D;
        return;
    }
    int st,nst;
    hm.init();
    memset(code,0,sizeof(code));
    code[0]=code[N-1]=1;
    hm.push(0);
    hm.push(encode(code,N));
    memset(g,0,sizeof(g));
    for(int i=1;i<hm.size;i++)
    {
        st=hm.state[i];
        for(nst=0;nst<(1<<N);nst++)
          if(check(st,nst))
          {
              int j=hm.push(encode(code,N));
              g[i][j]=1;
          }
    }
    D=hm.size;
    memcpy(node[N].g,g,sizeof(g));
    node[N].D=D;
}
void solve()
{
    Matrix temp;
    temp.n=temp.m=D;
    memcpy(temp.mat,g,sizeof(g));
    Matrix ans=pow_M(temp,M);
    if(ans.mat[1][0]==0)printf("Impossible\n");
    else printf("%d\n",ans.mat[1][0]%MOD);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    for(int i=0;i<20;i++)node[i].D=0;
    while(scanf("%d%d",&N,&M)==2)
    {
        init();
        solve();
    }
    return 0;
}
POJ 3133

/*
POJ 3133
连接2的插头为2,连接3的插头为3
没有插头为0
用四进制表示(四进制比三进制高效)

G++ 391ms
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXD=15;
const int HASH=10007;
const int STATE=1000010;
int N,M;
int maze[MAXD][MAXD];//0表示障碍,1是非障碍,2和3
int code[MAXD];
//0表示没有插头,2表示和插头2相连,3表示和插头3相连

struct HASHMAP
{
    int head[HASH],next[STATE],size;
    int state[STATE];
    int dp[STATE];
    void init()
    {
        size=0;
        memset(head,-1,sizeof(head));
    }
    void push(int st,int ans)
    {
        int i,h=st%HASH;
        for(i=head[h];i!=-1;i=next[i])
          if(state[i]==st)
          {
              if(dp[i]>ans)dp[i]=ans;
              return;
          }
        state[size]=st;
        dp[size]=ans;
        next[size]=head[h];
        head[h]=size++;
    }
}hm[2];
void decode(int *code,int m,int st)//四进制
{
    int i;
    for(int i=m;i>=0;i--)
    {
        code[i]=(st&3);
        st>>=2;
    }
}
int encode(int *code,int m)
{
    int i;
    int st=0;
    for(int i=0;i<=m;i++)
    {
        st<<=2;
        st|=code[i];
    }
    return st;
}
void shift(int *code,int m)//换行
{
    for(int i=m;i>0;i--)code[i]=code[i-1];
    code[0]=0;
}
void dpblank(int i,int j,int cur)
{
    int k,left,up;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        left=code[j-1];
        up=code[j];
        if(left&&up)
        {
            if(left==up)//只能是相同的插头
            {
                code[j-1]=code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
        }
        else if((left&&(!up))||((!left)&&up))//只有一个插头
        {
            int t;
            if(left)t=left;
            else t=up;//这里少写个else ,查了好久的错误
            if(maze[i][j+1]==1||maze[i][j+1]==t)//插头从右边出来
            {
                code[j-1]=0;
                code[j]=t;
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
            if(maze[i+1][j]==1||maze[i+1][j]==t)//插头从下边出来
            {
                code[j]=0;
                code[j-1]=t;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
        }
        else if(left==0&&up==0)//没有插头
        {
            code[j-1]=code[j]=0;//不加插头
            if(j==M)shift(code,M);
            hm[cur^1].push(encode(code,M),hm[cur].dp[k]);
            if(maze[i][j+1]&&maze[i+1][j])
            {
                if(maze[i][j+1]==1&&maze[i+1][j]==1)
                {
                    decode(code,M,hm[cur].state[k]);
                    code[j-1]=code[j]=2;//加2号插头
                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
                    //decode(code,M,hm[cur].state[k]);
                    code[j-1]=code[j]=3;//加3号插头
                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
                }
                else if((maze[i][j+1]==2&&maze[i+1][j]==1)||(maze[i+1][j]==2&&maze[i][j+1]==1)||(maze[i][j+1]==2&&maze[i+1][j]==2))
                {
                    decode(code,M,hm[cur].state[k]);
                    code[j-1]=code[j]=2;
                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
                }
                else if((maze[i][j+1]==3&&maze[i+1][j]==1)||(maze[i+1][j]==3&&maze[i][j+1]==1)||(maze[i][j+1]==3&&maze[i+1][j]==3))
                {
                    decode(code,M,hm[cur].state[k]);
                    code[j-1]=code[j]=3;
                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
                }
            }
        }
    }
}
void dpblock(int i,int j,int cur)
{
    int k;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        if(code[j-1]!=0||code[j]!=0)continue;
        code[j-1]=code[j]=0;//不加插头
        if(j==M)shift(code,M);
        hm[cur^1].push(encode(code,M),hm[cur].dp[k]);
    }
}
void dp_2(int i,int j,int cur)
{
    int left,up,k;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        left=code[j-1];
        up=code[j];
        if((left==2&&up==0)||(left==0&&up==2))
        {
            code[j-1]=code[j]=0;
            if(j==M)shift(code,M);
            hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
        }
        else if(left==0&&up==0)
        {
            if(maze[i][j+1]==1||maze[i][j+1]==2)
            {
                code[j-1]=0;
                code[j]=2;
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
            if(maze[i+1][j]==1||maze[i+1][j]==2)
            {
                code[j-1]=2;
                code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
        }
    }
}
void dp_3(int i,int j,int cur)
{
    int left,up,k;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        left=code[j-1];
        up=code[j];
        if((left==3&&up==0)||(left==0&&up==3))
        {
            code[j-1]=code[j]=0;
            if(j==M)shift(code,M);
            hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
        }
        else if(left==0&&up==0)
        {
            if(maze[i][j+1]==1||maze[i][j+1]==3)
            {
                code[j-1]=0;
                code[j]=3;
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
            if(maze[i+1][j]==1||maze[i+1][j]==3)
            {
                code[j-1]=3;
                code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);
            }
        }
    }
}

void init()
{
    memset(maze,0,sizeof(maze));
    for(int i=1;i<=N;i++)
      for(int j=1;j<=M;j++)
      {
          scanf("%d",&maze[i][j]);
          //if(maze[i][j]==0)maze[i][j]=1;
          //if(maze[i][j]==1)maze[i][j]=0;
          //上面的写法是错的,!!!
          if(maze[i][j]==1||maze[i][j]==0)maze[i][j]^=1;//0变1,1变0
      }
}
void solve()
{
    int i,j,cur=0;
    hm[cur].init();
    hm[cur].push(0,0);
    for(int i=1;i<=N;i++)
      for(int j=1;j<=M;j++)
      {
          hm[cur^1].init();
          if(maze[i][j]==0)dpblock(i,j,cur);
          else if(maze[i][j]==1)dpblank(i,j,cur);
          else if(maze[i][j]==2)dp_2(i,j,cur);
          else if(maze[i][j]==3)dp_3(i,j,cur);
          cur^=1;
      }
    int ans=0;
    for(int i=0;i<hm[cur].size;i++)
      ans+=hm[cur].dp[i];
    if(ans>0)ans-=2;
    printf("%d\n",ans);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(scanf("%d%d",&N,&M))
    {
        if(N==0&&M==0)break;
        init();
        solve();
    }
    return 0;
}

数据结构

后缀数组

找到出现k次可重叠最长子串

const int maxn = 2e5+100;
const int INF = 0x3f3f3f3f;
#define pr(x)       cout << #x << " = " << x << " ";
#define prln(x)     cout << #x << " = " << x <<endl;
#define rep(i,n) for(int i = 0;i < n; i++) 
#define ll long long
#define MEM(a,b) memset(a,b,sizeof a)
#define CLR(a) memset(a,0,sizeof a)
#define MN(a,b,n) memset(a,b,n*sizeof(int))
int rk[maxn], sa[maxn], height[maxn], w[maxn], wa[maxn], res[maxn];
void getSa(int len, int up){
    int *k = rk,*id = height, *r = res,*cnt=wa;
    rep(i,up) cnt[i] = 0;
    rep(i,len) cnt[k[i]=w[i]]++;
    rep(i,up) cnt[i+1] += cnt[i];
    for(int i = len-1; i >=0; --i){
        sa[--cnt[k[i]]] = i; 
    }
    int d = 1, p = 0;
    while(p < len) {
        for(int i = len-d; i < len;++i) id[p++] = i;
        rep(i,len) if(sa[i] >= d) id[p++] = sa[i]-d;
        rep(i,len) r[i] = k[id[i]];
        rep(i,up) cnt[i] = 0;
        rep(i,len) cnt[r[i]]++;
        rep(i,up) cnt[i+1] += cnt[i];
        for(int i = len-1; i >= 0; --i) {
            sa[--cnt[r[i]]] = id[i];
        }
        swap(k,r);
        p = 0;
        k[sa[0]] = p++;
        rep(i,len-1) {
            if(sa[i]+d < len && sa[i+1]+d <len &&r[sa[i]]==r[sa[i+1]]&&r[sa[i]+d]==r[sa[i+1]+d])
                k[sa[i+1]] = p-1;
            else    k[sa[i+1]] = p++;
        }
        if(p >= len) return;
        d*=2; up = p; p = 0;
    }
}
void getHeight(int len) {
    rep(i,len) rk[sa[i]] = i;
    height[0] = 0;
    for(int i = 0,p=0; i < len-1; ++i) {
        int j = sa[rk[i]-1];
        while(i+p<len&&j+p<len&&w[i+p]==w[j+p]) p++;
        height[rk[i]] = p;
        p = max(p-1,0);
    }
}
int getSuffix(int s[],int len) {
    int  up = 0;
    for(int i = 0; i < len; ++i) {
        w[i] = s[i];
        up = max(up,w[i]);
    }
    w[len++] = 0;
    getSa(len,up+1);
    getHeight(len);
    return len;
}
int n, k;
bool ok(int len) {
    int i = 2,  cnt;
    while(true) {
        while(i <= n && height[i] < len) ++i;
        if(i > n) return false;
        cnt = 1;
        while(i <= n && height[i] >= len) {
            ++i;
            cnt++;
        }
        if(cnt >= k) return true;
    }
    return false;
}
int num[maxn];
int main(){
#ifdef LOCAL
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
  //freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout);
 #endif

    cin >> n >> k;
    rep(i,n) scanf("%d",&num[i]);
    getSuffix(num,n);
    int l = 0, r= n, mid;
    while(l < r) {
        mid = (l+r+1)/2;
        if(ok(mid)) l = mid;
        else r = mid-1;
    }
    printf("%d\n",l);
    return 0;
}

AC自动机

在线AC自动机
/*
Educational Codeforces Round 16
给定N≤3×105次操作,操作一个字符串集合
1 s:向集合添加字符串s
2 s:从集合删除字符串s
3 s:查询字符串s在集合的所有字符串中出现了多少次
保证添加和删除操作合法,且∑|S|≤3×105
*/
const int N = 3e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;

struct ACAutomata {
    static const int S = 26;
    int sz, root;
    vector<vector<int> > nxt;
    vector<int> fail, cnt;

    inline int idx(char c) {return c - 'a';}
    inline int newNode() {
        cnt.push_back(0);
        nxt.push_back(vector<int>(S, 0));
        return sz++;
    }
    void init() {
        sz = 0;
        nxt.clear();
        cnt.clear();
        fail.clear();
        root = newNode();
    }
    void insert(const char* s, int d) {
        int u = root;
        for(; *s; ++s) {
            int c = idx(*s);
            int& v = nxt[u][c];
            if(!v) v = newNode();
            u = v;
        }
        cnt[u] += d;
    }
    void build() {
        vector<int> q;
        fail.resize(nxt.size());
        fail[root] = root;
        for(int i = 0; i < S; ++i) {
            int& v = nxt[root][i];
            if(v) {
                fail[v] = root;
                q.push_back(v);
            } else v = root;
        }
        for(int k = 0; k < q.size(); ++k) {
            int u = q[k];
            for(int i = 0; i < S; ++i) {
                int& v = nxt[u][i];
                if(v) {
                    fail[v] = nxt[fail[u]][i];
                    cnt[v] += cnt[nxt[fail[u]][i]];
                    q.push_back(v);
                } else v = nxt[fail[u]][i];
            }
        }
    }
    LL query(const char* s) {
        LL ret = 0;
        int u = root;
        for(; *s; ++s) {
            int c = idx(*s);
            u = nxt[u][c];
            ret += cnt[u];
        }
        return ret;
    }
};

int q, op[N];
string s[N];

struct StaticToDynamic {
    static const int LOG = 20;
    ACAutomata ac[LOG];
    vector<int> g[LOG];

    void init() {
        for(int i = 0; i < LOG; ++i) {
            g[i].clear();
            ac[i].init();
        }
    }
    inline get(int x) {
        return x == 1 ? 1 : -1;
    }
    void add(int id) {
        int p = -1;
        for(int i = 0; i < LOG && !~p; ++i) if(g[i].empty()) p = i;
        g[p].push_back(id);
        ac[p].insert(s[id].c_str(), get(op[id]));

        for(int i = 0; i < p; ++i) {
            for(int id : g[i]) {
                g[p].push_back(id);
                ac[p].insert(s[id].c_str(), get(op[id]));
            }
            g[i].clear();
            ac[i].init();
        }
        ac[p].build();
    }
    LL query(int id) {
        LL ret = 0;
        for(int i = 0; i < LOG; ++i) ret += ac[i].query(s[id].c_str());
        return ret;
    }
} solver;

int main() {


    ios_base::sync_with_stdio(0);

    while(cin >> q) {
        solver.init();
        for(int i = 1; i <= q; ++i) {
            cin >> op[i] >> s[i];
            if(op[i] <= 2) {
                solver.add(i);
            } else {
                cout << solver.query(i) << endl;
            }
        }
    }

    return 0;
}

回文树

/*************************************************************************
hdu5785
给定N≤106的字符串,现在寻找所有三元组(i, j, k),1≤i≤j<k≤N
使得s[i…j]和s[j+1…k]都是回文串,求∑∑i×k mod 109+7
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<cmath>
using namespace std;
#define ll long long
#define rep(i,n) for(int i =0; i < n; ++i)
#define CLR(x) memset(x,0,sizeof x)
#define MEM(a,b) memset(a,b,sizeof a)
#define pr(x) cout << #x << " = " << x << " ";
#define prln(x) cout << #x << " = " << x <<  endl; 
const int maxn = 1e6+100;
const int INF = 0x3f3f3f3f;
const int N = 27;
typedef pair<int,int> P;
const int MOD = 1e9+7;
struct Pstree{
    int nxt[maxn][26];
    int fail[maxn];
    int num[maxn];
    int len[maxn];
    int ans[maxn];
    char s[maxn];
    int last, n, p;
    int newnode(int x){
        for(int i = 0; i < N; ++i){
            nxt[p][i] = 0;
        }
        len[p] = x;
        num[p] = 0;
        ans[p] = 0;
        return p++;
    }
    void init(){
        p = 0;
        n = 0;
        last = 0;
        newnode(0);
        newnode(-1);
        s[n] = 255; 
        fail[0] = 1;
    }
    int getfail(int x){
        while(s[n-len[x]-1] != s[n]) x = fail[x];
        return x;
    }
    P add(int c){
        c -= 'a';
        s[++n] = c;
        int cur =  getfail(last);
        if(!nxt[cur][c]){
            int now = newnode(len[cur]+2);
            fail[now] = nxt[getfail(fail[cur])][c];
            nxt[cur][c] = now;
            num[now] =  num[fail[now]] + 1;
            ans[now] = ans[fail[now]] + len[cur]+2;
            ans[now] %= MOD;
        }
        last = nxt[cur][c];
        return P(num[last], ans[last]);
    }
}pstree;
char s[maxn];
int lnum[maxn];
int lans[maxn];
int main(){
    int n;
    while(scanf("%s", s) != EOF){
        n = strlen(s);
        pstree.init();
        for(int i = 0; i < n; ++i){
            P ans = pstree.add(s[i]);
            lnum[i+1] = ans.first;
            lans[i+1] = ans.second;
        }
        pstree.init();
        ll cnt = 0;
        for(int i = n-1; i > 0; --i){
            P ans = pstree.add(s[i]);
            cnt += ((ll)(i+1)*lnum[i] - (ll)lans[i])%MOD*((ll)((ll)i*ans.first%MOD) + ans.second)%MOD;
            cnt %= MOD;


        }
        cnt = (cnt%MOD+MOD)%MOD;
        printf("%lld\n", cnt);
    }
    return 0;
}
  • N≤103的字符串,Q≤105次询问

  • 每次询问[l, r]区间本质不同的回文子串有几个,即不完全相同的回文子串

/*hdu5658*/
const int N = 1e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

// last:= 指向添加一个字符后形成的最长回文串的节点
// s[i]:= 第 i 次添加的字符 n:= s 数组时针
// fail[i]:= i 失配后跳转到的 i 表示的最长回文串的最长真后缀回文串的节点
// cnt[i]:= 以 i 表示的最长回文串的右端点为右端点的回文串个数
// dif[i]:= i 表示的本质不同的回文串个数 (需要重新统计)
struct PalindromicTree {
    static const int M = 1e3 + 10, S = 26;
    int n, sz, last;
    int nxt[M][S], fail[M], len[M], s[M];
    int cnt[M], dif[M];

    int newNode(int l) {
        len[sz] = l;
        cnt[sz] = dif[sz] = 0;
        memset(nxt[sz], 0, sizeof nxt[sz]);
        return sz++;
    }

    void init() {
        sz = last = 0;
        newNode(0); newNode(-1);
        s[n = 0] = -1; // 无关字符减少特判
        fail[0] = 1;
    }

    int getFail(int u) {
        while(s[n - len[u] - 1] != s[n]) u = fail[u];
        return u;
    }

    void add(int c) {
        s[++n] = c;
        int u = getFail(last); // 找到这个回文串的匹配位置
        int& v = nxt[u][c];
        if(!v) {
            int cur = newNode(len[u] + 2);
            fail[cur] = nxt[getFail(fail[u])][c];
            v = cur;
            cnt[v] = cnt[fail[v]] + 1;
        }
        ++dif[v];
        last = v;
    }

    void count() {
        //父亲累加儿子,如果 fail[v]=u ,则 u 一定是 v 的子回文串
        for(int i = sz - 1; ~i; --i) dif[fail[i]] += dif[i];
    }
} pt;

int n, q;
char a[N];
int ans[N][N];

int main() {

    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%s", a + 1);
        n = strlen(a + 1);

        for(int i = 1; i <= n; ++i) {
            pt.init();
            for(int j = i; j <= n; ++j) {
                pt.add(a[j] - 'a');
                ans[i][j] = pt.sz - 2;
            }
        }

        scanf("%d", &q);
        while(q--) {
            int l, r; scanf("%d%d", &l, &r);
            printf("%d\n", ans[l][r]);
        }
    }
    return 0;
}

主席树

标记永久化
  • 给定N≤105个数,Q≤105询问,简而言之就是对某个版本进行区间更新区间查询
onst int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, q;

typedef long long LL;

int root[N];
struct PersistentSegTree {
    static const int M = 1e5 * 30;
    int sz;
    struct Node {
        int ls, rs, add;
        LL sum;
    } tree[M];
    void init() {
        sz = 0;
        memset(&tree[0], 0, sizeof tree[0]);
    }
    int newNode(int rt) {
        tree[++sz] = tree[rt];
        return sz;
    }
    void up(int rt) {
        tree[rt].sum = tree[tree[rt].ls].sum + tree[tree[rt].rs].sum;
    }
    void build(int l, int r, int& rt) {
        rt = newNode(0);
        if(l == r) {
            scanf("%I64d", &tree[rt].sum);
            return;
        }
        int m = l + r >> 1;
        build(l, m, tree[rt].ls);
        build(m + 1, r, tree[rt].rs);
        up(rt);
    }
    void show(int l, int r, int rt) {
        pr("show"); pr(rt); pr(l); pr(r); pr(tree[rt].add); prln(tree[rt].sum);
        if(l == r) return ;
        int m = l + r >> 1;
        show(l, m, tree[rt].ls);
        show(m + 1, r, tree[rt].rs);
    }
    void update(int L, int R, int v, int l, int r, int& rt) {
        rt = newNode(rt);
        tree[rt].sum += v * (min(r, R) - max(l, L) + 1);
        if(L <= l && r <= R) {
            tree[rt].add += v;
            return;
        }
        int m = l + r >> 1;
        if(L <= m) update(L, R, v, l, m, tree[rt].ls);
        if(R > m) update(L, R, v, m + 1, r, tree[rt].rs);
    }
    LL query(int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) return tree[rt].sum;
        LL ret = 1LL * tree[rt].add * (min(r, R) - max(l, L) + 1);
        int m = l + r >> 1;
        if(L <= m) ret += query(L, R, l, m, tree[rt].ls);
        if(R > m) ret += query(L, R, m + 1, r, tree[rt].rs);
        return ret;
    }
} T;

int main() {

    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &q) == 2) {
        T.init();
        T.build(1, n, root[0]);

        int timStp = 0;
        while(q--) {
            char op[2]; scanf("%s", op);
            if(*op == 'C') {
                int l, r, d; scanf("%d%d%d", &l, &r, &d);
                ++timStp;
                root[timStp] = root[timStp - 1];
//                T.show(1, n, root[timStp]);
                T.update(l, r, d, 1, n, root[timStp]);
            } else if(*op == 'Q') {
                int l, r; scanf("%d%d", &l, &r);
                printf("%I64d\n", T.query(l, r, 1, n, root[timStp]));
            } else if(*op == 'H') {
                int l, r, t; scanf("%d%d%d", &l, &r, &t);
                printf("%I64d\n", T.query(l, r, 1, n, root[t]));
            } else {
                scanf("%d", &timStp);
            }
        }
    }

    return 0;
}
prefix字典树 hdu5790
  • N≤105个字符串,保证∑|Li|≤105,Q≤105次询问在线查询[L, R]区间有多少个不同的前缀
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, q;
char s[N];

int root[N];
struct PersistentSegTree {
    static const int M = 1e5 * 40;
    int sz;
    struct Node {
        int ls, rs, sum;
    } tree[M];
    void init() {
        sz = 0;
        root[0] = 0;
    }
    int newNode(int rt) {
        tree[++sz] = tree[rt];
        return sz;
    }
    void update(int o, int v, int l, int r, int& rt) {
        rt = newNode(rt);
        tree[rt].sum += v;
        if(l == r) return;
        int m = l + r >> 1;
        if(o <= m) update(o, v, l, m, tree[rt].ls);
        else update(o, v, m + 1, r, tree[rt].rs);
    }
    int query(int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) return tree[rt].sum;
        int m = l + r >> 1, ret = 0;
        if(L <= m) ret += query(L, R, l, m, tree[rt].ls);
        if(R > m) ret += query(L, R, m + 1, r, tree[rt].rs);
        return ret;
    }
} T;

struct Trie {
    static const int M = 1e5 + 10, S = 26;
    int sz, rt, nxt[M][S];
    int val[M];
    int newNode() {
        val[sz] = 0;
        memset(nxt[sz], 0, sizeof nxt[sz]);
        return sz++;
    }
    void init() {
        sz = 0;
        rt = newNode();
    }
    void insert(char* s, int id) {
        int u = rt;
        for(int i = 0; s[i]; ++i) {
            int& v = nxt[u][s[i] - 'a'];
            if(!v) v = newNode();
            if(val[v]) T.update(val[v], -1, 1, n, root[id]);
            val[v] = id;
            T.update(val[v], 1, 1, n, root[id]);
            u = v;
        }
    }
} trie;

int main() {

    ios_base::sync_with_stdio(0);

    while(scanf("%d", &n) == 1) {
        T.init();
        trie.init();

        for(int i = 1; i <= n; ++i) {
            scanf("%s", s);
            root[i] = root[i - 1];
            trie.insert(s, i);
        }

        int z = 0;
        scanf("%d", &q);
        while(q--) {
            int l, r; scanf("%d%d", &l, &r);
            l = (z + l) % n + 1;
            r = (z + r) % n + 1;
            if(l > r) swap(l, r);
            z = T.query(l, n, 1, n, root[r]);
            printf("%d\n", z);
        }
    }

    return 0;
}

二叉搜索树

nlogn建树+kmp

N≤6×105,给定1∼N的序列,按照这个顺序建立一颗二叉搜索树
奇数是1,偶数是0,先序遍历这颗二叉搜索树生成1个01的欧拉序列
查找T串可重叠的出现了几次,|T|≤7000

const int N = 6e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m;
int rt, ls[N], rs[N];
char s[N << 1], t[7010];

void dfs(int rt) {
    s[n++] = (rt & 1) + '0';
    if(ls[rt]) {
        dfs(ls[rt]);
        s[n++] = (rt & 1) + '0';
    }
    if(rs[rt]) {
        dfs(rs[rt]);
        s[n++] = (rt & 1) + '0';
    }
}

int kmp() {
    m = strlen(t);
    vector<int> nxt(m + 1);
    nxt[0] = -1;
    for(int i = 0, j = -1; i < m;) {
        if(j == -1 || t[i] == t[j]) nxt[++i] = ++j;
        else j = nxt[j];
    }

    int ret = 0;
    for(int i = 0, j = 0; i < n;) {
        if(j == -1 || s[i] == t[j]) ++i, ++j;
        else j = nxt[j];
        if(j == m) {
            ++ret;
            j = nxt[j];
        }
    }
    return ret;
}

int main() {

    ios_base::sync_with_stdio(0);

    int T; scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        memset(ls, 0, sizeof ls);
        memset(rs, 0, sizeof rs);

        set<int> st;
        for(int i = 1; i <= n; ++i) {
            int x; scanf("%d", &x);
            if(!st.size()) rt = x;
            else {
                auto it = st.lower_bound(x);
                if(it == st.end()) rs[*--it] = x;
                else {
                    if(!ls[*it]) ls[*it] = x;
                    else rs[*--it] = x;
                }
            }
            st.insert(x);
        }
        scanf("%s", t);

        n = 0;
        dfs(rt);
        s[n] = 0;

        static int kase = 0;
        printf("Case #%d: %d\n", ++kase, kmp());
    }
    return 0;
}

可持久化Trie

HDU 5801 Up Sky,Mr.Zhu

给定N≤105的字符串S,字符集大小为5,其中的回文子串长度<20
定义回文子串str[0…n−1]的特征串为str[⌊n/2⌋…n−1]
给定询问区间s[L…R]里,特征串前缀为T的回文串有多少个,|T|≤10

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;

int root[N];
struct PersistentTrie {
    static const int M = N * 10, S = 5;
    int sz;
    struct Node {
        LL val;
        int nxt[S];
    } dat[M];
    void init() {
        sz = 0;
        memset(&dat[0], 0, sizeof dat[0]);
    }
    int newNode(int rt) {
        dat[++sz] = dat[rt];
        return sz;
    }
    void update(char* s, int n, int& rt) {
        rt = newNode(rt);
        int u = rt;
        for(int i = 0; i < n; ++i) {
            int c = s[i] - 'a';
            int& v = dat[u].nxt[c];
            v = newNode(v);
            ++dat[v].val;
            u = v;
        }
    }
    LL query(char* s, int rt) {
        int u = rt;
        for(int i = 0; s[i]; ++i) {
            int c = s[i] - 'a';
            u = dat[u].nxt[c];
        }
        return dat[u].val;
    }
} trie;

int q;
char s[N], t[N][15];
int l[N], r[N];
LL ans[N];

bool check(char* s, int len) {
    for(int i = 0; i < len / 2; ++i)
        if(s[-i] != s[-(len - i - 1)]) return false;
    return true;
}

int main() {

    ios_base::sync_with_stdio(0);

    while(scanf("%s%d", s + 1, &q) == 2) {
        int n = strlen(s + 1);
        for(int i = 1; i <= q; ++i) scanf("%d%d%s", l + i, r + i, t[i]);

        memset(ans, 0, sizeof ans);
        for(int len = 1; len < 20; ++len) {
            trie.init();
            for(int i = 1; i <= n; ++i) {
                root[i] = root[i - 1];
                if(i < len) continue;
                bool ok = check(s + i, len);
                if(!ok) continue;
                int l = i - (len - 1) / 2;
                trie.update(s + l, (len + 1) / 2, root[i]);
            }
            for(int i = 1; i <= q; ++i) {
                if(r[i] - l[i] + 1 < len) continue;
                ans[i] += trie.query(t[i], root[r[i]]) - trie.query(t[i], root[l[i] + len - 2]);
            }
        }
        for(int i = 1; i <= q; ++i)
            printf("%I64d\n", ans[i]);
    }
    return 0;
}

树上莫队

SPOJ Count on a tree II(树上莫队)

给定一颗N≤40000个点的树,点权Ai≤109
Q≤105,询问(u,v)路径上有多少不同的点权

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int B = 200;

int n, m, a[N], b[N];

struct Edge {
    int v, nxt;
} edge[N << 1];
int head[N], eCnt;

void addEdge(int u, int v) {
    edge[eCnt] = {v, head[u]};
    head[u] = eCnt++;
}

int L[N], R[N], dep[N], fa[N], vs[N], dfsNum;
int id[N], blocks;
int stk[N], top;

void dfs(int u, int f) {
    L[u] = ++dfsNum;
    vs[dfsNum] = u;
    fa[u] = f;

    int btm = top;
    for(int i = head[u]; ~i; i = edge[i].nxt) {
        int v = edge[i].v;
        if(v == f) continue;

        dep[v] = dep[u] + 1;
        dfs(v, u);

        if(top - btm >= B) {
            ++blocks;
            while(top != btm) {
                int v = stk[top--];
                id[v] = blocks;
            }
        }
    }
    R[u] = dfsNum;
    stk[++top] = u;
}

struct Query {
    int l, r, id;
};

bool cmpTree(const Query& a, const Query& b) {
    return id[a.l] < id[b.l] ||
           id[a.l] == id[b.l] && L[a.r] < L[b.r];
}

int cnt[N];
int sum, ans[N];

void add(int x) {
    if(cnt[b[x]]++ == 0) ++sum;
}

void del(int x) {
    if(--cnt[b[x]] == 0) --sum;
}

bool in[N];
int cross;
void reverse(int x) {
    if(in[x]) {
        in[x] = false;
        del(x);
    } else {
        in[x] = true;
        add(x);
    }
}

void moveUp(int& x) {
    if(!cross) {
        if(in[x] && !in[fa[x]]) cross = x;
        else if(in[fa[x]] && !in[x]) cross = fa[x];
    }
    reverse(x); x = fa[x];
}

void move(int a, int b) {
    if(a == b) return;
    cross = 0;
    if(in[b]) cross = b;
    while(dep[a] > dep[b]) moveUp(a);
    while(dep[b] > dep[a]) moveUp(b);
    while(a != b) moveUp(a), moveUp(b);
    reverse(a); reverse(cross);
}

void gao() {
    dfsNum = blocks = 0;
    dfs(1, 0);
    while(top) id[stk[top--]] = blocks;
}

int main() {

    ios_base::sync_with_stdio(0);

    scanf("%d%d", &n, &m);

    vector<int> xs(n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", a + i);
        xs[i - 1] = a[i];
    }
    sort(xs.begin(), xs.end());
    xs.resize(unique(xs.begin(), xs.end()) - xs.begin());
    for(int i = 1; i <= n; ++i)
        b[i] = lower_bound(xs.begin(), xs.end(), a[i]) - xs.begin() + 1;

    eCnt = 0; memset(head, -1, sizeof head);
    for(int i = 1; i < n; ++i) {
        int u, v; scanf("%d%d", &u, &v);
        addEdge(u, v);
        addEdge(v, u);
    }

    gao();

    vector<Query> qt(m);
    for(int i = 1; i <= m; ++i) {
        int u, v; scanf("%d%d", &u, &v);
        if(id[u] > id[v]) swap(u, v);
        qt[i - 1] = {u, v, i};
    }

    sort(qt.begin(), qt.end(), cmpTree);

    sum = 0;
    memset(cnt, 0, sizeof cnt);
    memset(in, 0, sizeof in);

    add(1); in[1] = true;
    int l = 1, r = 0;
    for(auto& q : qt) {
        move(l, q.l);
        move(r, q.r);
        l = q.l; r = q.r;

        ans[q.id] = sum;
    }
    for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);

    return 0;
}
HDU 5799 This world need more Zhu(树上莫队)

给定一颗N≤105个点的树,点权Ai≤109,Q≤105,询问形如opuvab
op=1时,u=v,查询子树u中,gcd(∑cnt[x]=ax,∑cnt[y]=by)
op=2时,查询路径(u,v)中,gcd(∑cnt[x]=ax,∑cnt[y]=by)

/*op=1,dfs序搞成序列上的莫队,op=2树上莫队*/
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int B = 250;

typedef long long LL;
int n, m, a[N], b[N];

struct Edge {
    int v, nxt;
} edge[N << 1];
int head[N], eCnt;

void addEdge(int u, int v) {
    edge[eCnt] = {v, head[u]};
    head[u] = eCnt++;
}

int L[N], R[N], dep[N], fa[N], vs[N], dfsNum;
int id[N], blocks;
int stk[N], top;

void dfs(int u, int f) {
    L[u] = ++dfsNum;
    vs[dfsNum] = u;
    fa[u] = f;

    int btm = top;
    for(int i = head[u]; ~i; i = edge[i].nxt) {
        int v = edge[i].v;
        if(v == f) continue;

        dep[v] = dep[u] + 1;
        dfs(v, u);

        if(top - btm >= B) {
            ++blocks;
            while(top != btm) {
                int v = stk[top--];
                id[v] = blocks;
            }
        }
    }
    R[u] = dfsNum;
    stk[++top] = u;
}

struct Query {
    int l, r, block, a, b, id;
};

bool cmpSeq(const Query& a, const Query& b) {
    return a.block < b.block ||
           a.block == b.block && a.r < b.r;
}
bool cmpTree(const Query& a, const Query& b) {
    return id[a.l] < id[b.l] ||
           id[a.l] == id[b.l] && L[a.r] < L[b.r];
}

int cnt[N];
LL sum[N], ans[N];

void add(int x) {
    sum[cnt[b[x]]] -= a[x];
    ++cnt[b[x]];
    sum[cnt[b[x]]] += a[x];
}

void del(int x) {
    sum[cnt[b[x]]] -= a[x];
    --cnt[b[x]];
    sum[cnt[b[x]]] += a[x];
}

bool in[N];
int cross;
void reverse(int x) {
    if(in[x]) {
        in[x] = false;
        del(x);
    } else {
        in[x] = true;
        add(x);
    }
}

void moveUp(int& x) {
    if(!cross) {
        if(in[x] && !in[fa[x]]) cross = x;
        else if(in[fa[x]] && !in[x]) cross = fa[x];
    }
    reverse(x); x = fa[x];
}

void move(int a, int b) {
    if(a == b) return;
    cross = 0;
    if(in[b]) cross = b;
    while(dep[a] > dep[b]) moveUp(a);
    while(dep[b] > dep[a]) moveUp(b);
    while(a != b) moveUp(a), moveUp(b);
    reverse(a); reverse(cross);
}

void gao() {
    dfsNum = blocks = 0;
    dfs(1, 0);
    while(top) id[stk[top--]] = blocks;
}

int main() {

    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);

        vector<int> xs(n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            xs[i - 1] = a[i];
        }
        sort(xs.begin(), xs.end());
        xs.resize(unique(xs.begin(), xs.end()) - xs.begin());
        for(int i = 1; i <= n; ++i)
            b[i] = lower_bound(xs.begin(), xs.end(), a[i]) - xs.begin() + 1;

        eCnt = 0; memset(head, -1, sizeof head);
        for(int i = 1; i < n; ++i) {
            int u, v; scanf("%d%d", &u, &v);
            addEdge(u, v);
            addEdge(v, u);
        }

        gao();

        vector<Query> qs, qt;
        for(int i = 1; i <= m; ++i) {
            int op, u, v, a, b;
            scanf("%d%d%d%d%d", &op, &u, &v, &a, &b);
            if(op == 1) {
                int p = L[u] / B;
                qs.push_back({L[u], R[u], p, a, b, i});
            } else {
                if(id[u] > id[v]) swap(u, v);
                qt.push_back({u, v, -1, a, b, i});
            }
        }

        {
            //subtree
            sort(qs.begin(), qs.end(), cmpSeq);
            memset(cnt, 0, sizeof cnt);
            memset(sum, 0, sizeof sum);

            int l = 1, r = 0;
            for(auto& q : qs) {
                while(r < q.r) add(vs[++r]);
                while(l < q.l) del(vs[l++]);
                while(r > q.r) del(vs[r--]);
                while(l > q.l) add(vs[--l]);

                ans[q.id] = __gcd(sum[q.a], sum[q.b]);
            }
        }

        {
            //path
            sort(qt.begin(), qt.end(), cmpTree);
            memset(cnt, 0, sizeof cnt);
            memset(sum, 0, sizeof sum);
            memset(in, 0, sizeof in);

            add(1); in[1] = true;
            int l = 1, r = 0;
            for(auto& q : qt) {
                move(l, q.l);
                move(r, q.r);
                l = q.l; r = q.r;

                ans[q.id] = __gcd(sum[q.a], sum[q.b]);
            }
        }

        static int kase = 0;
        printf("Case #%d:\n", ++kase);
        for(int i = 1; i <= m; ++i) printf("%I64d\n", ans[i]);
    }

    return 0;
}

线段树

AOJ 2450 Do use segment tree (树链剖分 + 线段树区间合并)

一颗N<=2×105的树,Q<=105,两种操作
1uvc,将u−>v路径上的点权变为c
2uvc,查询u−>v路径上的最大连续点权和,c卖萌的

const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, q, w[N], val[N];

struct Node {
    int l, r;
    int pre, suf, sub, sum;
    Node() {pre = suf = sub = sum = -INF;}
    Node rev() {
        Node ret = *this;
        swap(ret.pre, ret.suf);
        return ret;
    }
    void set(int v) {
        sum = v * (r - l + 1);
        pre = suf = sub = max(v, sum);
    }
};

void printNode(Node x) {
    printf("l: %d r: %d pre: %d suf: %d sub: %d sum: %d\n",
           x.l, x.r, x.pre, x.suf, x.sub, x.sum);
}

Node operator+ (const Node& A, const Node& B) {
    if(A.sub == -INF) return B;
    if(B.sub == -INF) return A;
    Node ret; ret.l = A.l, ret.r = B.r;
    ret.sum = A.sum + B.sum;
    ret.pre = max(A.pre, A.sum + B.pre);
    ret.suf = max(B.suf, B.sum + A.suf);
    ret.sub = max(A.suf + B.pre, max(A.sub, B.sub));
    return ret;
}

struct SegTree {
    Node dat[N << 2];
    int setv[N << 2];

    void push_up(int rt) {
        dat[rt] = dat[rt << 1] + dat[rt << 1 | 1];
    }

    void push_down(int rt) {
        if(setv[rt] != -INF) {
            int v = setv[rt], ls = rt << 1, rs = ls | 1;
            setv[ls] = setv[rs] = v;
            dat[ls].set(v);
            dat[rs].set(v);
            setv[rt] = -INF;
        }
    }

    void build(int l, int r, int rt) {
        setv[rt] = -INF;
        if(l == r) {
            dat[rt].l = l, dat[rt].r = r;
            dat[rt].set(val[l]);
            return;
        }
        int m = l + r >> 1;
        build(l, m, rt << 1);
        build(m + 1, r, rt << 1 | 1);
        push_up(rt);
    }

    void update(int L, int R, int v, int rt) {
        if(L <= dat[rt].l && dat[rt].r <= R) {
            setv[rt] = v;
            dat[rt].set(v);
            return;
        }
        push_down(rt);
        int m = dat[rt].l + dat[rt].r >> 1;
        if(L <= m) update(L, R, v, rt << 1);
        if(R > m) update(L, R, v, rt << 1 | 1);
        push_up(rt);
    }

    void printAll(int rt) {
        printNode(dat[rt]);
        if(dat[rt].l == dat[rt].r) return;
        printAll(rt << 1);
        printAll(rt << 1 | 1);
    }

    Node query(int L, int R, int rt) {
        if(L <= dat[rt].l && dat[rt].r <= R) return dat[rt];
        push_down(rt);
        int m = dat[rt].l + dat[rt].r >> 1;
        Node ret;
        if(L <= m && R > m) ret = query(L, R, rt << 1) + query(L, R, rt << 1 | 1);
        else if(L <= m) ret = query(L, R, rt << 1);
        else if(R > m) ret = query(L, R, rt << 1 | 1);
        return ret;
    }
};

struct Edge {
    int to, nxt;
};

struct HLD {
    int head[N], cnt, tid;
    int sz[N], son[N], fa[N], dep[N], top[N], dfn[N];
    Edge edge[N << 1];
    SegTree T;

    void init() {
        cnt = tid = 0;
        memset(head, -1, sizeof head);
        memset(son, 0, sizeof son);
    }

    void add_edge(int u, int v) {
        edge[cnt] = (Edge) {v, head[u]};
        head[u] = cnt++;
        edge[cnt] = (Edge) {u, head[v]};
        head[v] = cnt++;
    }
    //find heavy edge
    void dfs1(int u, int f, int d) {
        sz[u] = 1, fa[u] = f, dep[u] = d;
        for(int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].to;
            if(v == f) continue;
            dfs1(v, u, d + 1);
            if(!son[u] || sz[v] > sz[son[u]]) son[u] = v;
            sz[u] += sz[v];
        }
    }
    //connect heavy edge
    void dfs2(int u, int tp) {
        dfn[u] = ++tid;
        val[tid] = w[u];
        top[u] = tp;
        if(son[u]) dfs2(son[u], tp);
        for(int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].to;
            if(v == fa[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }

    void update(int u, int v, int c) {
        int fu = top[u], fv = top[v];
        while(fu != fv) {
            if(dep[fu] < dep[fv]) {
                swap(u, v);
                swap(fu, fv);
            }
            T.update(dfn[fu], dfn[u], c, 1);
            u = fa[fu];
            fu = top[u];
        }
        if(dep[u] > dep[v]) swap(u, v);
        T.update(dfn[u], dfn[v], c, 1);
    }

    int query(int u, int v) {
        Node L, R;  //there [l,r] info doesn't matter
        int fu = top[u], fv = top[v];
        while(fu != fv) {
            if(dep[fu] > dep[fv]) {
                L = L + T.query(dfn[fu], dfn[u], 1).rev();
                u = fa[fu], fu = top[u];
            } else {
                R = T.query(dfn[fv], dfn[v], 1) + R;
                v = fa[fv], fv = top[v];
            }
        }
        if(dep[u] > dep[v]) L = L + T.query(dfn[v], dfn[u], 1).rev();
        else R = T.query(dfn[u], dfn[v], 1) + R;
        return (L + R).sub;
    }

    void build() {
        dfs1(1, -1, 0);
        dfs2(1, 1);
        T.build(1, n, 1);
    }
};

HLD hld;

int main() {

    while(scanf("%d%d", &n, &q) == 2) {
        hld.init();
        for(int i = 1; i <= n; ++i) scanf("%d", w + i);
        for(int i = 1; i < n; ++i) {
            int u, v; scanf("%d%d", &u, &v);
            hld.add_edge(u, v);
        }
        hld.build();
        while(q--) {
            int op, x, y, z; scanf("%d%d%d%d", &op, &x, &y, &z);
            if(op == 1) hld.update(x, y, z);
            else printf("%d\n", hld.query(x, y));
        }
    }
    return 0;
}
HDU 5737 Differencia(归并树)

N≤105长度的A,B两个数组,Ai,Bi≤109
Q≤3×106次查询,2种查询
+lrx:把A数组的[l,r]区间数变为x
?lr:查询[l,r]区间Ai≥Bi的下标个数

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, q, A, B;
int a[N], b[N];

int rnd(int& last, int& a, int& b) {
    int C = ~(1 << 31), M = (1 << 16) - 1;
    a = (36969 + (last >> 3)) * (a & M) + (a >> 16);
    b = (18000 + (last >> 3)) * (b & M) + (b >> 16);
    return (C & ((a << 16) + b)) % 1000000000;
}

namespace Discretization {
    vector<int> xs;
    void init() {
        xs = vector<int>(b + 1, b + 1 + n);
        sort(xs.begin(), xs.end());
        for(int i = 1; i <= n; ++i) {
            b[i] = lower_bound(xs.begin(), xs.end(), b[i]) - xs.begin() + 1;
            a[i] = upper_bound(xs.begin(), xs.end(), a[i]) - xs.begin();
        }
    }
    int get(int x) {
        return upper_bound(xs.begin(), xs.end(), x) - xs.begin();
    }
}

namespace Allocator {
    int data[N << 6], *p;
    void init() {
        p = data;
    }
    int* allocate(int len) {
        p += len;
        return p - len;
    }
}

struct Node {
    int* indexLeft, *indexRight;
    int tag, sum;

    void setTag(int v) {
        tag = sum = v;
    }
    int goLeft(int v) {
        if(v) return indexLeft[v];
        return 0;
    }
    int goRight(int v) {
        if(v) return indexRight[v];
        return 0;
    }

} tree[N << 2];

void pushUp(int rt) {
    tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}

void pushDown(int rt) {
    if(~tree[rt].tag) {
        int v = tree[rt].tag;
        int ls = rt << 1, rs = ls | 1;
        tree[ls].setTag(tree[rt].goLeft(v));
        tree[rs].setTag(tree[rt].goRight(v));
        tree[rt].tag = -1;
    }
}

void merge(int rt, int l, int r) {
    static int tmp[N];
    int m = l + r >> 1;
    int* vl = b + l - 1, *vr = b + m;
    int sl = m - l + 1, sr = r - m;
    int i = 1, j = 1, k = 1;
    while(i <= sl && j <= sr) {
        if(vl[i] < vr[j]) {
            tree[rt].indexLeft[k] = i;
            tree[rt].indexRight[k] = j - 1;
            tmp[k++] = vl[i++];
        } else {
            tree[rt].indexLeft[k] = i - 1;
            tree[rt].indexRight[k] = j;
            tmp[k++] = vr[j++];
        }
    }
    while(i <= sl) {
        tree[rt].indexLeft[k] = i;
        tree[rt].indexRight[k] = j - 1;
        tmp[k++] = vl[i++];
    }
    while(j <= sr) {
        tree[rt].indexLeft[k] = i - 1;
        tree[rt].indexRight[k] = j;
        tmp[k++] = vr[j++];
    }
    memcpy(b + l, tmp + 1, r - l + 1 << 2);
}

void build(int l, int r, int rt) {
    tree[rt].tag = -1;
    tree[rt].indexLeft = Allocator::allocate(r - l + 1);
    tree[rt].indexRight = Allocator::allocate(r - l + 1);
    if(l == r) {
        tree[rt].sum = a[l] >= b[l];
        return;
    }
    int m = l + r >> 1;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    pushUp(rt);
    merge(rt, l, r);
}

void update(int L, int R, int v, int l, int r, int rt) {
    if(L <= l && r <= R) {
        tree[rt].setTag(v);
        return;
    }
    int m = l + r >> 1;
    pushDown(rt);
    if(L <= m) update(L, R, tree[rt].goLeft(v), l, m, rt << 1);
    if(R > m) update(L, R, tree[rt].goRight(v), m + 1, r, rt << 1 | 1);
    pushUp(rt);
}

int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return tree[rt].sum;
    int m = l + r >> 1, ret = 0;
    pushDown(rt);
    if(L <= m) ret += query(L, R, l, m, rt << 1);
    if(R > m) ret += query(L, R, m + 1, r, rt << 1 | 1);
    return ret;
}

int main() {

    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d%d", &n, &q, &A, &B);
        for(int i = 1; i <= n; ++i) scanf("%d", a + i);
        for(int i = 1; i <= n; ++i) scanf("%d", b + i);

        Allocator::init();
        Discretization::init();
        build(1, n, 1);

        int ans = 0, last = 0;
        for(int i = 1; i <= q; ++i) {
            int l = rnd(last, A, B) % n + 1;
            int r = rnd(last, A, B) % n + 1;
            int x = rnd(last, A, B) + 1;
            if(l > r) swap(l, r);
            if(l + r + x & 1) {
                x = Discretization::get(x);
//                printf("+ %d %d %d\n", l, r, x);
                update(l, r, x, 1, n, 1);
            } else {
                last = query(l, r, 1, n, 1);
//                printf("? %d %d ans = %d\n", l, r, last);
                ans += 1LL * i * last % MOD;
                if(ans >= MOD) ans -= MOD;
            }
        }
        printf("%d\n", ans);
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}
UVA 11402 Ahoy, Pirates!(线段树标记合并)

Fab:[a,b]变为1
Eab:[a,b]变为0
Iab:[a,b]01翻转,即0变1,1变0
Sab:[a,b]中1有多少个

const int N = 1.1e6 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

string str;

struct Node {
    int l, r;
    int sum;
    Node() {}
    Node(int l, int r): l(l), r(r) {}
    int len() {
        return r - l + 1;
    }
    void set(int v) {
        if(v == -1) return;
        if(v == 2) sum = len() - sum;
        else sum = len() * v;
    }
} dat[N << 2];

int tag[N << 2];

void pushUp(int rt) {
    dat[rt].sum = dat[rt << 1].sum + dat[rt << 1 | 1].sum;
}

void combineTag(int fa, int& son) {
    if(fa == 2) {
        if(son == -1) son = 2;
        else if(son == 2) son = -1;
        else son ^= 1; // switch 0, 1
    } else son = fa; //set 0, 1
}

void pushDown(int rt) {
    if(tag[rt] == -1) return;

    int ls = rt << 1, rs = ls | 1;
    dat[ls].set(tag[rt]);
    dat[rs].set(tag[rt]);
    combineTag(tag[rt], tag[ls]);
    combineTag(tag[rt], tag[rs]);

    tag[rt] = -1;
}

void build(int l, int r, int rt) {
    dat[rt] = Node(l, r);
    tag[rt] = -1;
    if(l == r) {
        dat[rt].sum = str[l] - '0';
        return;
    }
    int m = l + r >> 1;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    pushUp(rt);
}

void update(int L, int R, int v, int rt) {
    if(L <= dat[rt].l && dat[rt].r <= R) {
        dat[rt].set(v);
        combineTag(v, tag[rt]);
        return;
    }
    pushDown(rt);
    int m = dat[rt].l + dat[rt].r >> 1;
    if(L <= m) update(L, R, v, rt << 1);
    if(R > m) update(L, R, v, rt << 1 | 1);
    pushUp(rt);
}

int query(int L, int R, int rt) {
    if(L <= dat[rt].l && dat[rt].r <= R) return dat[rt].sum;
    pushDown(rt);
    int m = dat[rt].l + dat[rt].r >> 1;
    int ret = 0;
    if(L <= m) ret += query(L, R, rt << 1);
    if(R > m) ret += query(L, R, rt << 1 | 1);
    return ret;
}

int main() {

    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        str.clear();
        int m; scanf("%d", &m);
        while(m--) {
            int cnt;
            char buf[105]; scanf("%d%s", &cnt, buf);
            while(cnt--) str += buf;
        }
        build(0, str.size() - 1, 1);

        int q; scanf("%d", &q);
        int qs = 0;
        static int kase = 0;
        printf("Case %d:\n", ++kase);
        while(q--) {
            char op[2]; int a, b; scanf("%s%d%d", op, &a, &b);
            if(*op == 'F') update(a, b, 1, 1);
            else if(*op == 'E') update(a, b, 0, 1);
            else if(*op == 'I') update(a, b, 2, 1);
            else printf("Q%d: %d\n", ++qs, query(a, b, 1));
        }
    }
    return 0;
}

POJ 3168 Barn Expansion(扫描线)

给定N≤2.5×104个矩形,保证没有重合,且任何边相切或者顶点重合的是不好的
问最后还有几个好的

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n;
int bx[N], by[N], ux[N], uy[N];

struct Line {
    int x, y, id;
    Line() {}
    Line(int x, int y, int id): x(x), y(y), id(id) {}
    bool operator<(const Line& l) const {
        if(x == l.x) {
            if(y == l.y) return id < l.id; //+ first
            return y < l.y;
        }
        return x < l.x;
    }
};

void solve(vector<int>& bad, int* bx, int* ux, int* by, int* uy) {
    vector<Line> events;
    for(int i = 1; i <= n; ++i) {
        events.push_back(Line(bx[i], by[i], -i));
        events.push_back(Line(bx[i], uy[i], i));
        events.push_back(Line(ux[i], by[i], -i));
        events.push_back(Line(ux[i], uy[i], i));
    }
    sort(events.begin(), events.end());

    int sum = 1;
    for(int i = 1; i < events.size(); ++i) {
        sum += events[i].id < 0 ? 1 : -1;
        int preID = abs(events[i - 1].id), curID = abs(events[i].id);
        if(sum >= 2) bad[preID] = bad[curID] = true;
    }
}

int main() {

    ios_base::sync_with_stdio(0);

    while(scanf("%d", &n) == 1) {
        for(int i = 1; i <= n; ++i)
            scanf("%d%d%d%d", bx + i, by + i, ux + i, uy + i);

        vector<int> bad(n + 1, false);
        solve(bad, bx, ux, by, uy);
        solve(bad, by, uy, bx, ux);

        int ans = n - count(bad.begin(), bad.end(), true);
        printf("%d\n", ans);
    }

    return 0;
}

数学

FFT

HDU 5730 Shell Necklace(dp、cdq分治+FFT)

给定N≤105个贝壳的项链,每连续i≤N个贝壳模式的贡献是ai
对于某种串项链的方式,假设含有模式b1,b2,⋯,bm,总贡献为∏mi=1abi
求所有串项链方式的贡献和

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 313;
const double PI = acos(-1);

int n, a[N];
int f[N];

typedef complex<double> Complex;

void rader(Complex* y, int len) {
    for(int i = 1, j = len / 2; i < len - 1; i++) {
        if(i < j) swap(y[i], y[j]);
        int k = len / 2;
        while(j >= k) {j -= k; k /= 2;}
        if(j < k) j += k;
    }
}
void fft(Complex* y, int len, int op) {
    rader(y, len);
    for(int h = 2; h <= len; h <<= 1) {
        double ang = op * 2 * PI / h;
        Complex wn(cos(ang), sin(ang));
        for(int j = 0; j < len; j += h) {
            Complex w(1, 0);
            for(int k = j; k < j + h / 2; k++) {
                Complex u = y[k];
                Complex t = w * y[k + h / 2];
                y[k] = u + t;
                y[k + h / 2] = u - t;
                w = w * wn;
            }
        }
    }
    if(op == -1) for(int i = 0; i < len; i++) y[i] /= len;
}

Complex A[N << 1], B[N << 1];

void cdq(int l, int r) {
    if(l == r) return;
    int mid = (l + r) >> 1;
    cdq(l, mid);

    int len = 1;
    while(len <= r - l + 1) len <<= 1;
    for(int i = 0; i < len; i++) {
        A[i] = Complex(l + i <= mid ? f[l + i] : 0, 0);
        B[i] = Complex(l + i + 1 <= r ? a[i + 1] : 0, 0);
    }
    fft(A, len, 1);
    fft(B, len, 1);
    for(int i = 0; i < len; i++) A[i] *= B[i];
    fft(A, len, -1);

    for(int i = mid + 1; i <= r; i++) {
        f[i] += fmod(A[i - l - 1].real(), MOD) + 0.5;
        if(f[i] >= MOD) f[i] -= MOD;
    }
    cdq(mid + 1, r);
}

int main() {

    ios_base::sync_with_stdio(0);


    while(scanf("%d", &n) == 1 && n) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            a[i] %= MOD;
        }

        memset(f, 0, sizeof f);
        f[0] = 1;
        cdq(0, n);
        printf("%d\n", f[n]);
    }

    return 0;
}

NTT

HDU 5829 Rikka with Subset (NTT)

给定N≤105个数,对于一个给定的1≤K≤N,设数集全集为U
任意S∈U,val(S):=S中前min(K,|S|)大数的和
val(U)k=∑S∈Uval(S)
输出每个val(U)k

/*
//ntt(A, len, 1);
//ntt(B, len, 1);
//for(int i = 0; i < len; ++i) A[i] = A[i] * B[i] % P;
//ntt(A, len, -1);
//(r ^{2k}) + 1     rr  kk  gg
3   1   1   2
5   1   2   2
17  1   4   3
97  3   5   5
193 3   6   5
257 1   8   3
7681    15  9   17
12289   3   12  11
40961   5   13  3
65537   1   16  3
786433  3   18  10
5767169 11  19  3
7340033 7   20  3
23068673    11  21  3
104857601   25  22  3
167772161   5   25  3
469762049   7   26  3
1004535809  479 21  3
2013265921  15  27  31
2281701377  17  27  3
3221225473  3   30  5
75161927681 35  31  3
77309411329 9   33  7
206158430209    3   36  22
2061584302081   15  37  7
2748779069441   5   39  3
6597069766657   3   41  5
39582418599937  9   42  5
79164837199873  9   43  5
263882790666241 15  44  7
1231453023109121    35  45  3
1337006139375617    19  46  3
3799912185593857    27  47  5
4222124650659841    15  48  19
7881299347898369    7   50  6
31525197391593473   7   52  3
180143985094819841  5   55  6
1945555039024054273 27  56  5
4179340454199820289    29   57  3
*/
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
const int G = 3, P = 998244353;

LL quick(LL x, LL n) {
    LL ret = 1;
    for(; n; n >>= 1) {
        if(n & 1) ret = ret * x % P;
        x = x * x % P;
    }
    return ret;
}

LL A[N << 2], B[N << 2];
void rader(LL* y, int len) {
    for(int i = 1, j = len / 2; i < len - 1; i++) {
        if(i < j) swap(y[i], y[j]);
        int k = len / 2;
        while(j >= k) {j -= k; k /= 2;}
        if(j < k) j += k;
    }
}
void ntt(LL* y, int len, int op) {
    rader(y, len);
    for(int h = 2; h <= len; h <<= 1) {
        LL wn = quick(G, (P - 1) / h);
        if(op == -1) wn = quick(wn, P - 2);
        for(int j = 0; j < len; j += h) {
            LL w = 1;
            for(int k = j; k < j + h / 2; k++) {
                LL u = y[k];
                LL t = w * y[k + h / 2] % P;
                y[k] = (u + t) % P;
                y[k + h / 2] = (u - t + P) % P;
                w = w * wn % P;
            }
        }
    }
    if(op == -1) {
        LL inv = quick(len, P - 2);
        for(int i = 0; i < len; i++) y[i] = y[i] * inv % P;
    }
}

int n, a[N], f[N];
LL fact[N], invf[N], two[N], invt[N];

int main() {

    ios_base::sync_with_stdio(0);

    fact[0] = two[0] = invf[0] = invt[0] = 1;
    for(int i = 1; i < N; ++i) {
        fact[i] = fact[i - 1] * i % P;
        two[i] = two[i - 1] * 2 % P;
        invf[i] = quick(fact[i], P - 2);
        invt[i] = quick(two[i], P - 2);
    }

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) scanf("%d", a + i);
        sort(a + 1, a + n + 1, greater<int>());

        int len = 1;
        while(len <= n << 1) len <<= 1;
        for(int i = 0; i < len; ++i) {
            A[i] = i <= n ? two[n - i] * invf[i] % P : 0;
            B[i] = i >= 1 && i <= n ? a[i] * fact[i - 1] % P : 0;
        }
        reverse(B + 1, B + n + 1);

        ntt(A, len, 1);
        ntt(B, len, 1);
        for(int i = 0; i < len; ++i) A[i] = A[i] * B[i] % P;
        ntt(A, len, -1);

        for(int i = 1; i <= n; ++i) f[i] = invt[i] * invf[i - 1] % P * A[n - i + 1] % P;
        for(int i = 1; i <= n; ++i) if((f[i] += f[i - 1]) >= P) f[i] -= P;
        for(int i = 1; i <= n; ++i) printf("%d ", f[i]);
        puts("");
    }

    return 0;
}

FWT

IForg 1028 Bob and Alice are playing numbers

n个数里选2个数进行三种位运算的最大值
数的大小只有106,cnt[i]:=i这个数出现了多少次
然后卷积一下自己,减去自己和自己的,倒着枚举找到最大的那个就做完了

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;

LL quick(LL x, LL n) {
    LL ret = 1;
    for(; n; n >>= 1) {
        if(n & 1) ret = ret * x % MOD;
        x = x * x % MOD;
    }
    return ret;
}

const LL invTwo = quick(2, MOD - 2);

void fwtXor(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    fwtXor(a, h);
    fwtXor(a + h, h);
    for(int i = 0; i < h; ++i) {
        LL x1 = a[i];
        LL x2 = a[i + h];
        a[i] = (x1 + x2) % MOD;
        a[i + h] = (x1 - x2 + MOD) % MOD;
    }
}

void ifwtXor(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    for(int i = 0; i < h; ++i) {
        // y1=x1+x2
        // y2=x1-x2
        LL y1 = a[i];
        LL y2 = a[i + h];
        a[i] = (y1 + y2) * invTwo % MOD;
        a[i + h] = (y1 - y2 + MOD) * invTwo % MOD;
    }
    ifwtXor(a, h);
    ifwtXor(a + h, h);
}

void fwtAnd(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    fwtAnd(a, h);
    fwtAnd(a + h, h);
    for(int i = 0; i < h; ++i) {
        LL x1 = a[i];
        LL x2 = a[i + h];
        a[i] = (x1 + x2) % MOD;
        a[i + h] = x2;
    }
}

void ifwtAnd(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    for(int i = 0; i < h; ++i) {
        // y1=x1+x2
        // y2=x2
        LL y1 = a[i];
        LL y2 = a[i + h];
        a[i] = (y1 - y2 + MOD) % MOD;
        a[i + h] = y2;
    }
    ifwtAnd(a, h);
    ifwtAnd(a + h, h);
}

void fwtOr(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    fwtOr(a, h);
    fwtOr(a + h, h);
    for(int i = 0; i < h; ++i) {
        LL x1 = a[i];
        LL x2 = a[i + h];
        a[i] = x1;
        a[i + h] = (x2 + x1) % MOD;
    }
}

void ifwtOr(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    for(int i = 0; i < h; ++i) {
        // y1=x1
        // y2=x2+x1
        LL y1 = a[i];
        LL y2 = a[i + h];
        a[i] = y1;
        a[i + h] = (y2 - y1 + MOD) % MOD;
    }
    ifwtOr(a, h);
    ifwtOr(a + h, h);
}

int n, op;
const int C = 1 << 20;
LL a[N], cnt[C + 10];

int gao() {
    for(int i = C; ~i; --i) if(cnt[i]) return i;
    return -1;
}

int main() {

    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &op);
        memset(cnt, 0, sizeof cnt);
        for(int i = 1; i <= n; ++i) {
            scanf("%lld", a + i);
            ++cnt[a[i]];
        }

        static int kase = 0;
        printf("Case #%d: ", ++kase);
        if(op == 1) {
            fwtAnd(cnt, C);
            for(int i = 0; i < C; ++i) cnt[i] = cnt[i] * cnt[i] % MOD;
            ifwtAnd(cnt, C);
            for(int i = 1; i <= n; ++i) --cnt[a[i] & a[i]];
            printf("%d\n", gao());
        } else if(op == 2) {
            fwtXor(cnt, C);
            for(int i = 0; i < C; ++i) cnt[i] = cnt[i] * cnt[i] % MOD;
            ifwtXor(cnt, C);
            for(int i = 1; i <= n; ++i) --cnt[a[i] ^ a[i]];
            printf("%d\n", gao());
        } else {
            fwtOr(cnt, C);
            for(int i = 0; i < C; ++i) cnt[i] = cnt[i] * cnt[i] % MOD;
            ifwtOr(cnt, C);
            for(int i = 1; i <= n; ++i) --cnt[a[i] | a[i]];
            printf("%d\n", gao());
        }
    }

    return 0;
}
SRM 518 NIM

题意:
2个人玩nim游戏,能选K≤109堆,每堆必须是素数pi≤L≤106,后手赢的方案数

分析:
nim游戏,由SG定理知,先手xorsum为0输,即后手赢
问题就变成了这个,之后就可以dp了,f[i][j]:=选i堆异或和为j的方法数
显然f[1][j]是知道的,转移是f[i][j]=∑x⊕y=jf[i−1][x]×f[1][y]
发现这是个and卷积的形式,答案就是卷积的k次幂,所以直接做就好了

fwtXor(a, L)
a[i] = a[i] ^ k
ifwtXor(a, L)
ans = a[0]
Codeforces 449D Jzzhu and Numbers
  • 题意:
    • 给定长度为N≤106的数列,Ai≤106,选出0
const int N = 1e6 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;

void fwtAnd(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    fwtAnd(a, h);
    fwtAnd(a + h, h);
    for(int i = 0; i < h; ++i) {
        LL x1 = a[i];
        LL x2 = a[i + h];
        a[i] = (x1 + x2) % MOD;
        a[i + h] = x2 % MOD;
    }
}

void ifwtAnd(LL* a, int len) {
    if(len == 1) return;
    int h = len >> 1;
    for(int i = 0; i < h; ++i) {
        // y1=x1+x2
        // y2=x2
        LL y1 = a[i];
        LL y2 = a[i + h];
        a[i] = (y1 - y2 + MOD) % MOD;
        a[i + h] = y2 % MOD;
    }
    ifwtAnd(a, h);
    ifwtAnd(a + h, h);
}

int n;
const int C = 1 << 20;
LL cnt[C + 10], two[C + 10];

int main() {

    ios_base::sync_with_stdio(0);

    two[0] = 1;
    for(int i = 1; i < C; ++i) two[i] = two[i - 1] * 2 % MOD;

    while(scanf("%d", &n) == 1) {
        memset(cnt, 0, sizeof cnt);
        for(int i = 1; i <= n; ++i) {
            int x; scanf("%d", &x);
            ++cnt[x];
        }

        fwtAnd(cnt, C);
        for(int i = 0; i < C; ++i) cnt[i] = two[cnt[i]];
        ifwtAnd(cnt, C);
        printf("%I64d\n", cnt[0]);
    }

    return 0;
}

其他

树Hash

HDU 5732 Subway(树哈希)

给定N≤105个节点的2棵树,保证2棵树同构
输出一种2棵树的节点映射方式

#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
const LL seed[2] = {999999937, 999999929};
const LL mod[2] = {1000000007, 1000000009}; //1e9+7/+9
struct Hash {
    LL h[2];
    Hash() {}
    Hash(const LL& first) {
        for(int i = 0; i < 2; ++i) h[i] = first;
    }
    Hash operator+(const Hash& rhs) const {
        Hash ret = *this;
        for(int i = 0; i < 2; ++i) {
            ret.h[i] = ret.h[i] * seed[i] % mod[i];
            if((ret.h[i] += rhs.h[i]) >= mod[i]) ret.h[i] -= mod[i];
        }
        return ret;
    }
    bool operator<(const Hash& rhs) const {
        return make_pair(h[0], h[1]) < make_pair(rhs.h[0], rhs.h[1]);
    }
    bool operator==(const Hash& rhs) const {
        return make_pair(h[0], h[1]) == make_pair(rhs.h[0], rhs.h[1]);
    }

    void see() {
        printf("(%llu %llu)\n", h[0], h[1]);
    }
};

int n;
vector<int> G[2][N];

void getDiameter(int u, int f, int d, int idx, pair<int, int>& diameter) {
    diameter = max(diameter, make_pair(d, u));
    vector<int>& cur = G[idx][u];
    for(auto& v : cur) {
        if(v == f) continue;
        getDiameter(v, u, d + 1, idx, diameter);
    }
}

bool getPath(int u, int f, int t, int idx, vector<int>& path) {
    path.push_back(u);
    if(u == t) return true;
    vector<int>& cur = G[idx][u];
    for(auto& v : cur) {
        if(v == f) continue;
        if(getPath(v, u, t, idx, path)) return true;
    }
    path.pop_back();
    return false;
}

vector<pair<Hash, int> > treeHash[2][N];

Hash getHash(int u, int f, int idx) {
    vector<int>& cur = G[idx][u];
    vector<pair<Hash, int> > sons;
    for(auto& v : cur) {
        if(v == f) continue;
        sons.push_back({getHash(v, u, idx), v});
    }

    Hash h(1);
    sort(sons.begin(), sons.end());
    for(auto& v : sons) h = h + v.first;
    treeHash[idx][u].swap(sons);
    return h;
}

map<string, int> mp[2];
vector<string> names[2];
int ID(string s, int idx) {
    if(mp[idx].count(s)) return mp[idx][s];
    names[idx].push_back(s);
    mp[idx][s] = names[idx].size();
    return mp[idx][s];
}

void init(int idx) {
    mp[idx].clear(); names[idx].clear();
    for(int i = 1; i <= n; ++i) G[idx][i].clear();
}

void OLE(string s) {
    while(1)
        puts(s.c_str());
}

void RE() {
    printf("%d\n", *((int*)0));
}

bool match(vector<pair<Hash, int> >& cur, vector<pair<Hash, int> >& nxt,
           vector<pair<int, int> >& ans) {
    if(cur.size() != nxt.size()) return false;
    int cnt = 0;
    for(int i = 0; i < cur.size(); ++i) {
        bool ok = false;
        for(int j = i; j < nxt.size() && cur[i].first == nxt[j].first; ++j) {
            swap(nxt[i], nxt[j]);
            if(match(treeHash[0][cur[i].second], treeHash[1][nxt[i].second], ans)) {
                ++cnt;
                ok = true;
                ans.push_back({cur[i].second, nxt[i].second});
                break;
            }
        }
        if(!ok) {
            while(cnt--) ans.pop_back();
            return false;
        }
    }
    return true;
}

int main() {

    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    while(scanf("%d", &n) == 1) {
        for(int i = 0; i < 2; ++i) {
            init(i);

            for(int j = 1; j < n; ++j) {
                char a[20], b[20]; scanf("%s%s", a, b);
                int u = ID(a, i), v = ID(b, i);
//                printf("%d->%d\n", u, v);
                G[i][u].push_back(v);
                G[i][v].push_back(u);
            }
//            puts("");
        }

        vector<pair<Hash, int> > hashes[2];
        for(int i = 0; i < 2; ++i) {
            pair<int, int> diameter = { -INF, -INF};
            getDiameter(1, -1, 0, i, diameter);
            int u = diameter.second;
            diameter = { -INF, -INF};
            getDiameter(u, -1, 0, i, diameter);
            int v = diameter.second;

//            printf("diameter %d -> %d\n", u, v);

            vector<int> path;
            getPath(u, -1, v, i, path);
            int sz = path.size();
            int x = path[sz >> 1], y = -1;
            if(~path.size() & 1) y = path[sz / 2 - 1];
            hashes[i].push_back({getHash(x, y, i), x});
            if(~y) hashes[i].push_back({getHash(y, x, i), y});

//            printf("start %d %d\n", x, y);

            sort(hashes[i].begin(), hashes[i].end());
        }

        vector<pair<int, int> > ans;
        if(!match(hashes[0], hashes[1], ans)) RE();

//        prln(ans.size());
        if(ans.size() != n) OLE("WA");

        for(int i = 0; i < ans.size(); ++i) {
            int u, v; tie(u, v) = ans[i];
            printf("%s %s\n", names[0][u - 1].c_str(), names[1][v - 1].c_str());
        }
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}
VK Cup 2016 - Round 1 D. Bear and Polynomials(哈希)

N≤2×105,给定一个N次多项式,即P(x)=∑Ni=0ai⋅xi
已经P(2)≠0,现要改变其中一个系数ai,使得P′(2)=0
求方法数

/* 
由于N非常大,所以直接算是不行的
我们可以通过对素数取模来哈希这个结果,这个素数一定要比max{ai}大
取2个素数,之后枚举每个系数,算出答案,比较是否相等即可,同时别忘记负数答案了
各种取模,注意不要模出负数了
时间复杂度O(n)
*/
const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, k;
int a[N];
typedef long long LL;

LL ksm(LL x, LL n, LL MOD) {
    LL ret = 1;
    for(; n; n >>= 1) {
        if(n & 1) ret = ret * x % MOD;
        x = x * x % MOD;
    }
    return ret;
}

int main() {

    ios_base::sync_with_stdio(0);

    scanf("%d%d", &n, &k);
    LL sum[2] = {}, mod[2] = {MOD, MOD + 2};
    LL power[2] = {1, 1}, base[2] = {2, 2};
    for(int i = 0; i <= n; ++i) {
        scanf("%d", a + i);
        for(int j = 0; j < 2; ++j) {
            sum[j] = (sum[j] + a[i] * power[j] % mod[j]) % mod[j];
            sum[j] = (sum[j] + mod[j]) % mod[j];
            power[j] = power[j] * base[j] % mod[j];
        }
    }

    int ans = 0;
    for(int i = 0; i < 2; ++i) power[i] = 1, base[i] = ksm(2, mod[i] - 2, mod[i]);
    for(int i = 0; i <= n; ++i) {
        LL delta[2];
        for(int j = 0; j < 2; ++j) {
            delta[j] = (a[i] - sum[j] * power[j] % mod[j]) % mod[j];
            delta[j] = (delta[j] + mod[j]) % mod[j];
            power[j] = power[j] * base[j] % mod[j];
        }

        LL cof = INF;
        if(delta[0] == delta[1]) cof = delta[0];
        if(delta[0] - mod[0] == delta[1] - mod[1]) cof = delta[0] - mod[0];
        if(abs(cof) > k || i == n && cof == 0) continue;

        ++ans;
    }
    printf("%d\n", ans);
    return 0;
}
Codeforces Round 354 (Div. 2) E. The Last Fight Between Human and AI

给定一个N≤105次多项式P(x)=anxn+an−1xn−1+...+a1x+a0
现2个人轮流随意填P(x)的系数,填过的不能再填
现有一个多项式Q(x)=x−k,如果最终多项式P(x)可以整除Q(x),那么后手赢
有一部分系数数已经被2个人填了,?表示还没填
现2个人最优操作的情况下,问后手是否能赢

/* 假如所有系数ai都是确定的,如果P(x)能被x−k整除
说明x−k是P(x)的一个因子,显然x=k是P(x)=0的一个根,k≠0
那么讨论一下k=0的情况:
如果a[0]=0,那么Q(x)已经整除P(x),反之则不能整除
如果a[0]不确定,那么第一个填a[0]的人胜
再仔细看一下k≠0的情况:
如果系数全部确定的情况上面已经说了,代入x=k判断P(x)=0成立性
对于不完全确定的情况,我们先看一下只有1个不确定的情况:
令其他的确定系数的项的和为A,不确定的那项为aixi
由P(x)=A+aixi=0得,ai=−Axi,由于ai可以是实数
那么最后一步的操作者有必胜策略
这样分2种情况讨论,每种情况里又有2种情况讨论这题就做完了
对于如何算出P(k),实际上只有从高位到低位乘起来
如果中间大于N×max{Ai}那么就无解了,然后判是不是0就好了
当然对于zz选手的我,直接取很多素数对结果取模进行哈希判断也是可以哒
*/

const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, k;
int a[N];

int calc(int mod) {
    int ret = 0;
    for(int i = n; ~i; --i) ret = (1LL * ret * k + a[i]) % mod;
    return ret;
}

int main() {


    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    while(scanf("%d%d", &n, &k) == 2) {
        int unknown = 0;
        for(int i = 0; i <= n; ++i) {
            char buf[10]; scanf("%s", buf);
            a[i] = *buf == '?' ? INF : atoi(buf);
            unknown += a[i] == INF;
        }

        int used = n + 1 - unknown;

        if(k == 0) {
            //a[0] is 0 will win
            puts(a[0] == 0 || a[0] == INF && (used & 1) ? "Yes" : "No");
        } else {
            if(!unknown) {
                bool ok = true;
                for(int i = 2e9; ; ++i) {
                    if(calc(i)) {ok = false; break;}
                    if(1.0 * (clock() - _) / CLOCKS_PER_SEC > 0.95) break;
                }
                puts(ok ? "Yes" : "No");
            } else puts(~(n + 1) & 1 ? "Yes" : "No");
        }
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}

生成树计数

给定无向图3个点A、B、G,AB间有a条边,AG间有b条边,BG间有c条边
求从A出发回到A的欧拉回路的个数,答案模109+7

/*
叉姐给出1个有向图欧拉回路计数的定理
有向图欧拉回路的话,判定条件:连通,每个点入度=出度
有向图欧拉回路计数(BSET Theorem):
ec(G)=ts(G)⋅deg(s)!⋅∏v∈V, v≠s(deg(v)−1)!, ts(G):=以s为根的外向树的个数
注意特判1个点答案是1
生成树计数(Kirchhoff Theorem):
基尔霍夫矩阵K=度数矩阵D−邻接矩阵A
重边:按照边数计算,自环:不计入度数
无向图生成树计数:c=|K的任意1个n−1阶主子式|
有向图外向树计数:c=|去掉根所在的那阶得到的主子式|
以上是学习内容,这个题只要枚举一条边的其中1个方向的边数
然后根据欧拉回路判定性条件解出其他边的2个方向的边数
然后直接套定理解出个数,注意选边的时候要乘组合数
然后这个题就做完了,时间复杂度O(n)
*/
const int N = 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
LL a[N][N], ans[N];
bool isFreeX[N];

LL gauss(int n, int m) {
    for(int i = 0; i < m; ++i) isFreeX[i] = false;
    LL ret = 1, neg = 0;

    int r = 1, c = 1;
    for(; r < n && c < m; ++r, ++c) {
        int p = r;
        for(; p < n; ++p) if(a[p][c]) break;
        if(p == n) {--r; isFreeX[c] = true; continue;}
        if(p != r) {
            neg ^= 1;
            for(int i = c; i <= m; ++i) swap(a[p][i], a[r][i]);
        }

        //eliminate coefficient
        for(int i = r + 1; i < n; ++i) {
            while(a[i][c]) {
                LL delta = a[i][c] / a[r][c];
                for(int j = c; j <= m; ++j) {
                    a[i][j] += MOD - delta * a[r][j] % MOD;
                    a[i][j] %= MOD;
                }
                if(!a[i][c]) break;
                neg ^= 1;
                for(int j = c; j <= m; ++j) swap(a[r][j], a[i][j]);
            }
        }
    }
    if(r != n) return 0;
    for(int i = 1; i < r; ++i) ret = ret * a[i][i] % MOD;
    if(neg) ret = (-ret + MOD) % MOD;
    return ret;
}

int A, B, C;
int deg[N];

bool check(int& x, int A) {
    if(x & 1) return false;
    x /= 2;
    return x >= 0 && x <= A;
}

const int M = 1e5 + 10;
LL fact[M], finv[M];

LL quick(LL x, LL n) {
    LL ret = 1;
    for(; n; n >>= 1) {
        if(n & 1) ret = ret * x % MOD;
        x = x * x % MOD;
    }
    return ret;
}

LL comb(int n, int m) {
    if(n < m) return 0;
    return fact[n] * finv[m] % MOD * finv[n - m] % MOD;
}

int main() {

    ios_base::sync_with_stdio(0);

    fact[0] = finv[0] = 1;
    for(int i = 1; i < M; ++i) {
        fact[i] = fact[i - 1] * i % MOD;
        finv[i] = quick(fact[i], MOD - 2);
    }

    while(scanf("%d%d%d", &A, &B, &C) == 3) {
        LL ans = 0;
        for(int x = 0; x <= A; ++x) { //x in-degrees from A; y from C, z from B
            int y = 2 * x + C - A;
            if(!check(y, C)) continue;
            int z = 2 * y + B - C;
            if(!check(z, B)) continue;
            if(x + B - z != A - x + z) continue; //check A

            deg[0] = x + B - z;
            deg[1] = y + A - x;
            deg[2] = z + C - y;
            for(int i = 0; i < 3; ++i) a[i][i] = deg[i];
            a[0][1] = -(A - x); a[0][2] = -z;
            a[1][0] = -x;  a[1][2] = -(C - y);
            a[2][0] = -(B - z); a[2][1] = -y;

            LL cur = comb(A, x) * comb(C, y) % MOD * comb(B, z) % MOD;

            //BEST Theorem
            cur = cur * gauss(3, 3) % MOD;
            cur = cur * deg[0] % MOD;
            for(int i = 0; i < 3; ++i) cur = cur * fact[deg[i] - 1] % MOD;
            ans = (ans + cur) % MOD;
        }
        printf("%lld\n", ans);
    }

    return 0;
}
ACM MM 2025论文排版避坑指南:Overleaf模板从安装到提交全流程 本文详细介绍了ACM MM 2025论文排版的全流程,重点讲解了Overleaf模板的正确使用方法,包括模板选择、元数据编排、内容排版和编译提交等关键步骤,帮助研究者避开常见错误,确保论文格式符合会议要求。 阅读详情

相关推荐

ACM MM2024 Rebuttal Latex模板如何去掉标题

Latex初学者,非官方指定做法,仅供大家参考。

qiao1025566574的博客 3851

ACM MM 2024论文投稿模板

Overleaf 平台上的模板都是单栏的,给的Word版本是双栏的,建议使用第一种方式。图片中红色框出的是模板里自带的,官网没有对此的明确说明,但大家是默认删除。进行修改,如图为原始生成的PDF。会议一般都是双盲审。这个注释打开,并修改为自己注册的号码。的DOI信息记得摘要截止提交后修改。此时就是基础匿名版本,如下图所示。删除2,在tex添加以下语句。祝新的一年,多发paper,点,添加以下代码,即可删除。基本投稿的模板就准备好了。

wuli_xin的博客 2万+

ACM算法竞赛常用模板与实战

ACM国际大学生程序设计竞赛(ACM-ICPC)是一项历史悠久的计算机竞赛,旨在挑战参赛者的算法和编程能力。竞赛要求选手在限定时间内解决一系列涉及算法、数据结构、计算数学等多方面知识的问题,强调团队合作与快速准确的编码能力。图论模板在解决图的遍历、连通性、最短路径和最大流问题时具有极高的效率和灵活性。DFS和BFS是图的两个基本遍历算法,可以应用于不同的图分析任务。SCC和割点算法是理解图的结构和分割图的关键。

weixin_28681379的博客 1024

ACM MM会议-latex模板使用

ACM MM会议-latex模板使用模板的使用公式的编辑行内公式行间公式编号公式单行多行图片插入 latex模板踩坑记录 模板的使用 投稿是ACM 下的 multi mudie 会议 , 使用sample-sigconf模板。新建一个文件夹将acmart-master中acmart.cls 、acmart.bib、ACM-Reference-Format.bst及samples文件夹下sigconf拷入,编译即可。报错是因为没有考入sample下的图片sample-franklin.png。另外。Texst

qq_44941389的博客 8782

ACM会议模板

ACM会议模板的Latex。Unofficial Template for New Format of ACM Conference Proceedings (LaTeX) This is an unofficial template that can be used to create proceedings for ACM conferences in LaTeX format.

Halcon实战:5分钟搞定线序颜色检测(附完整代码解析)

本文详细介绍了如何使用Halcon在5分钟内实现工业级线序颜色检测的高效方案。通过机器视觉技术,结合实战项目经验,提供完整的代码解析和优化技巧,帮助开发者快速构建鲁棒性强、准确率高的检测系统,适用于电子制造和汽车线束等行业。

weixin_29098367的博客 440

ACM算法模板 · 一些常用的算法模板-模板合集(打比赛专用)

ACM算法模板 · 一些常用的算法模板-模板合集(打比赛专用)

无论多么困难,也要熬下去!!! 682

【传感器标定(三):激光雷达标定原理与实现详解】

本文系统介绍了激光雷达标定的基本原理与方法。主要内容包括:激光雷达标定的分类(内参标定和外参标定)、坐标系定义和刚体变换数学基础;详细阐述了测距误差和光束方向两种内参标定方法;重点分析了基于目标(平面标定板、三维标定物)和基于运动(手眼标定、点云配准)的外参标定方法;介绍了多传感器联合标定技术及非线性优化、鲁棒标定等实现方法;最后提出了定量和定性两种标定评估体系。为激光雷达在自动驾驶等领域的精确应用提供了系统的标定技术指导。

weixin_47429781的博客 3789

ACM常用算法模板文档

ACM常用算法模板文档 去发现同类优质开源项目:https://gitcode.com/ 本仓库提供的资源文件为《ACM常用算法模板.docx》,这是一个Word文档,其中包含了ACM竞赛中常用的算法和数据结构模板。该文档旨在帮助参赛者快速掌握竞赛所需的核心算法,提高编程效率。 文件类型:Word文档(.docx) 内容概述:涵盖ACM竞赛中常用的各类算法和数据结构模板,方便选手在比赛中迅速调用...

gitblog_06745的博客 457

【CQUT】从零开始龙芯杯-开始前的准备

不同级别的Cpu对于vivado综合的时间有较大影响,其中Cpu的单核、多核性能以及cache性能影响权重较高。因此,为了保证学习进度和效率,我们建议在Cpu不低于I5 12400的环境下进行学习。Vivado2023.2安装包约100GB,为了保证成功安装建议留出300GB硬盘空间。龙芯杯的许多资料被托管在gitee,你需要git指令将资料克隆到本地。指定用书:CPU设计实战 Loongarch版 作者:汪文祥、邢金璋。Cpu性能对比网站:https://www.cpu114.com/

weixin_73689971的博客 906

从S参数矩阵到硬件实现:手把手教你用耦合微带线设计Marchand巴伦

本文详细介绍了使用耦合微带线设计Marchand巴伦的完整流程。从奇偶模理论和S参数矩阵分析入手,结合ADS仿真软件进行参数优化与电磁仿真验证,最终指导PCB布局、制作与实测调试。文章以2.4GHz Wi-Fi前端电路为例,手把手演示了从理论计算到硬件实现的全过程,并分享了关键的工程经验与调试技巧。

weixin_29271263的博客 536

基于C++实现的ACM竞赛常用模板

基于C++实现的ACM竞赛常用模板 去发现同类优质开源项目:https://gitcode.com/ 此仓库提供了一套基于C++编写的ACM竞赛常用模板,旨在帮助竞赛选手在比赛中快速搭建程序框架,节省宝贵的时间。本模板包含了多种常用的算法和数据结构,适用于各类ACM-ICPC竞赛场景。 模板内容 排序算法:快速排序、归并排序、堆排序等 搜索算法:深度优先搜索、广度优先搜索等 图算法:最短路径、最...

gitblog_06743的博客 485

Ta-Lib源码解析(三):蜡烛图指标 (Candlestick Indicator) #(附Python重构代码和示意图)(补充中)

蜡烛图指标为识别K线形态的指标,笔者认为重点在于定义方式所提供的思路,背后的投资逻辑可以借鉴但不建议直接参考文档的翻译内容和源码逻辑存在不一致的现象,笔者分别列出中文百科中的定义和源码实际逻辑,供大家参考技术分析理论中的一些模糊词,ta-lib中给出了精确的定义源码

量化就是探索生活 3254

ACM常用模板

前言 注意事项: 1、当需要读取大量的数据,推荐使用scanf printf 2、字符串最好使用char []来存储,不要用string,否则可能会占用大量内存及减低速度 常用头文件 #define _CRT_SBCURE_NO_DEPRECATE #include <bits/stdc++.h> #include <iostream> #include &lt...

qq_42024195的博客 610

内存学习(3):DRAM的基础存储结构(存储层级、读写过程,刷新与暂存)

DRAM,全称为 Dynamic Random Access Memory ,中文名是“动态随机存取存储器”。所谓“动态”是和“静态”相对应的,芯片世界里还有一种 SRAM 静态随机存取存储器的存在。笼统地说,DRAM 的结构比 SRAM 更简单,面积占用更小,适合制作大容量的存储芯片;而 SRAM 结构复杂一些,一般使用六个晶体管,面积消耗大,但是读写速度快,而且因为 SRAM 只用到晶体管,所以在工艺上和逻辑芯片相兼容,我们可以在逻辑芯片上直接集成 SRAM。

呆呆象呆呆的博客 2万+

UE5 Niagara基础知识讲解

在UE5版本中,Niagara粒子系统引入了许多可添加的模块,分别用于控制发射器的生成和更新、粒子的生成和更新以及渲染效果。本文采用虚幻5.2.1版本,以图文方式对Niagara基础知识进行讲解。

一碗清深的博客 2468

ACM常用算法模板

常用模板/算法总结 文章目录常用模板/算法总结@[toc]卢卡斯求组合数欧拉筛快速阶乘取模欧拉函数(欧拉筛版) 卢卡斯求组合数 /* (C n,m) % p */ #include&lt;cstdio&gt; #include&lt;iostream&gt; using namespace std; #define ll long long ll n, m, p; ll pow_m(ll a...

Dawn-K の Blog 619

PCL DBSCAN密度聚类【2026最新版】

点云密度聚类的PCL代码实现,与matlab自带的密度聚类算法进行对比验证,结果一致。3万多个点能再2秒左右运行出结果,算法效率尚可。

点云侠的博客 5681

ACM JAVA+C常用方法模板

//测试时间!!!!!! 放开头:long star=System.currentTimeMillis(); 放结尾:System.err.println(System.currentTimeMillis()-star);   //JAVA将字符串拆分放入数租.(截取字符串:截取第i个位置到第i+1个位置的字符串) String a = "abc"; String[] arr = ne

乌合 1943

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

ACM常用算法模板文档:助你编程竞赛一举夺魁

ACM常用算法模板文档:助你编程竞赛一举夺魁 去发现同类优质开源项目:https://gitcode.com/ 项目的核心功能/场景 ACM竞赛算法和数据结构模板,编程效率提升利器。 项目介绍 在编程竞赛的世界里,ACM(国际大学生程序设计竞赛)无疑是最具挑战性和影响力的赛事之一。为了在竞赛中脱颖而出,掌握核心算法和数据结构至关重要。今天,我要为大家推荐的开源项目——“ACM常用算法模板文档”,它...

gitblog_06741的博客 431

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

ACM自用常用模板(c++版)

最近几次比赛每次都被一些题目坑到超时,所以把每次看到比较优秀的模板给抄下来,留着备用。前面:typedef long long LL; //#define MOD xxx const int MOD=xxx;1.排列组合公式//C(m,n)LL C(int m,int n) { int k=1;//相当于C(m,n) LL ans=1; while(k<=n) {

RainbowCat的博客 4104

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ACM常用模板套用

ACM常用模板素数判定组合计数函数 素数判定 常规法 返回的类型是bool型,时间复杂度是O( sqrt(n) ); bool isprime(int x) { if(x==1) return false; for(int i= 2; i <= sqrt(x);++i) { if(x%i == 0) return false; } return true; } 欧拉筛法 时间复杂度最小O(n) 。 数开的过大也会TL。 调用函数 prime(MAX); 可以求MAX以内的素数,存放在

liuchenwill的博客 382

ACM常用模板(持续更新)

数学 快速沃尔什变换 int qpow(int x,int k,int mo) { int s = 1; while(k) { if(k&1) s=s*x%mo; x=x*x%mo; k>>=1; }return s; } void FMT(int *a,int n,int op) { int inv2 = qpow(2ll,mod-2,mod); for(int k=1;k<n;k<<=1) for(int i=0;i&lt

OIer,ACMer,专注于 c++/编译器/虚拟机/AI学习 886
上一篇: JAVA 项目笔记
下一篇: Qt5.8 ubuntu下面连接mysql
Vanguard-xf
博客等级 码龄12年 40粉丝 64原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值