北航2016集训队选拔赛解题报告

Preface

https://biancheng.love/contest-ng/index.html#/80

A zxa and split

Description

在一根长度无限的数轴上,有 n 个位置生活着一种神奇的二次元生物zxa。

每经过一年,一只zxa会分裂成两只,若它的坐标为x,则两只新的zxa分别产生在坐标为 (x1) (x+1) 的位置,然后原来的那只zxa消失。

然而,由于生存空间有限,若一个位置上有不少于 P 只zxa,将立刻消失P只zxa。

经过测定, P=109+7

请你预计,第 T 年的位置w上有多少只zxa。(最初是第 0 年)

Input

输入包含多组测试数据,以EOF结束,不超过10组数据。

对于每组测试数据:

第一行包含三个整数n,T w

接下来n行,每行两个整数 Xi,Ci ,表示位置 Xi 上最初有 Ci 只zxa。

0n,T,|w|105,0|Xi|T,0<Ci105

Output

对于每组测试数据输出一行,包含一个非负整数,表示第 T w位置上有多少只zxa。

Sample Input

2 2 2
0 3
1 2

Sample Output

3

Hint

012200310320326123420233002

题解

很容易发现是组合数,预处理下阶乘和逆元的前缀和, O(1) 计算下组合数即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;
const LL mod=1e9+7;
LL f[110000],inv[110000],ff[110000];
LL n,T,w;
LL C(int n,int m)
{
    if (m>n) return 0;
    LL res=(f[n]*inv[m])%mod;
    res=(res*inv[n-m]) %mod;
    return res;
}
int main()
{
    f[0]=1;
    for (int i=1;i<=100000;i++)
        f[i]=(i*f[i-1])%mod;
    ff[1]=ff[0]=inv[1]=inv[0]=1;
    for (int i=2;i<=100000;i++)
    {
        inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
        ff[i]=inv[i];
    }
    for (int i=2;i<=100000;i++)
    {
        inv[i]=(inv[i]*inv[i-1])%mod;
    }    
    while (cin>>n>>T>>w)
    {
          LL ans=0;
          for (int i=1;i<=n;i++)
          {
              LL x,y;
              cin>>x>>y;
              LL d=abs(w-x);
              LL num=0;
              if (T%2==0)
              {
                  if (d%2==1) continue;
                  LL pos=T/2+1;
                  pos+=d/2;
                  num=(num+C(T,pos-1))%mod;
              }
              else
              {
                  if (d%2==0) continue;
                  LL pos=(T+1)/2;
                  pos+=d/2;
                  num=(ans+C(T,pos-1))%mod;
              }
              num=(num*y)%mod;
              ans=(ans+num)%mod;
          }
          printf("%lld\n",ans);
    }
    return 0;
}

B zxa and kids

Description

zxa马上就要毕业了!

作为一个即将离开BUAA的人,zxa想为母校再做一些微小的贡献,于是他计划带领北航幼儿园的小朋友出去玩。

zxa现在要带领 n 个小朋友过一条河,zxa作为蓝桥杯特等奖的获得者,有一种神奇的魔法,可以创建一个蓝色的桥横跨河的两岸,从而使得zxa和小朋友能够通过这个桥到达河对岸。

由于小朋友的年龄非常小,如果独自通过桥的话会有掉下去的危险,因此必须由zxa陪着他们来过河。

但是zxa的体重太大了,因此每次最多只能带一个小朋友到河对岸。也就是说,zxa只能带一个小朋友过河,之后再自己从桥上走回来带另一个小朋友过河。

现在已知北航幼儿园的小朋友有n个,从 0 (n1)编号,另给出 m 个二元组(xi,yi),表示编号为 xi 的小朋友和编号为 yi 的小朋友之间有一些矛盾,他们只要一见面就会打架。

但是zxa有独特的人格魅力,只要zxa在他们身边(和他们在同一个河岸上),他们就不会打架。

zxa想使所有小朋友都能够到河的对岸且在任意时刻都不会有小朋友打架,他想知道这能不能做到。

zxa觉得一个人来做太累了,他创建了 k 个分身来协助他,他的每个分身都和他有同样的体重和人格魅力。

也就是说,最开始河的一岸有n个小朋友和 (k+1) 个zxa,每次一个zxa可以带至多一个小朋友过河,之后他再返回继续带至多一个小朋友过河,或者不再返回,由其他zxa带领小朋友过河。他想知道是否能把所有小朋友带到河对岸且任意时刻都不会有小朋友打架。

Input

输入包含多组测试数据,以EOF结束,不超过30组数据。

对于每组测试数据:

第一行包含三个非负整数 n,m k ,表示有n个小朋友,有 m 对小朋友之间有矛盾,zxa能创建k个分身。

接下来 m 行,每行包含两个非负整数xi,yi,表示小朋友 xi 和小朋友 yi 之间有矛盾。小朋友不会自己与自己有矛盾,但是同样的矛盾在描述中可能会给出多次。
1n200,0m600,0k8,0x,y<n

Output

对于每组测试数据输出一行,如果zxa能完成这个任务则输出YES,否则输出NO。

Sample Input

2 1 0
0 1

Sample Output

YES

题解

k=0 ,不难发现,如果存在两个及以上的本质不同的矛盾关系,zxa一定不能带小朋友起飞;反之,第一次就带矛盾中的一个小朋友过河,之后都带不是矛盾中另一个小朋友的小朋友过河,最后再带那个特别的小朋友过河即可。若 k=1 ,YY下可以得出这样一个结论。如果小盆友之间的关系图,如果去掉两个点后可二分,则输出YES。分身先带走二分图中的X集合,然后再回来,带走其中之一的特殊点。zxa自己先将第二个特殊点带过河,然后再将Y集合带过河。这样能满足题意。 k>1 时,让两个分身分别站在两端,zxa每次带小朋友过河即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
int n,m,k;
int last[210],e[1300],pre[1300];
int v[210],pp[210];
int xx,yy;
struct node
{
    int x,y;
}a[610];
int cmp(node a,node b)
{
    if (a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}
int flag;
void dfs(int x,int y)
{
    v[x]=y;
    pp[x]=1;
    for (int i=last[x];i>0;i=pre[i])
    {
        if (!pp[e[i]]&&e[i]!=xx&&e[i]!=yy) dfs(e[i],3-y);
        else
        {
            if (v[e[i]]==v[x]&&e[i]!=xx&&e[i]!=yy)
            {
                flag=1;
                break;
            }
        }
    }
}
int main()
{
    memset(e,0,sizeof(e));
    while (scanf("%d %d %d",&n,&m,&k)==3)
    {
        memset(last,0,sizeof(last));
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            if (x>y) swap(x,y);
            e[i*2]=y;
            pre[i*2]=last[x];
            last[x]=i*2;
            e[i*2-1]=x;
            pre[i*2-1]=last[y];
            last[y]=i*2-1;
            a[i].x=x,a[i].y=y;
        }
        sort(a+1,a+1+m,cmp);
        int num=0;
        if (k==0)
        {
            if (m!=0) num=1;
            for (int i=2;i<=m;i++)
            {
                if (a[i].x!=a[i-1].x||a[i].y!=a[i-1].y) num++;
            }
            if (num>1) printf("NO\n");
            else printf("YES\n");
        }
        else if (k==1)
        {
            int fflag=0;
            for (int i=0;i<n;i++)
                for (int j=i+1;j<n;j++)
                {
                    memset(v,0,sizeof(v));
                    memset(pp,0,sizeof(pp));
                    flag=0;
                    xx=i,yy=j;
                    for (int l=0;l<n;l++)
                    {
                        if (l==i||l==j) continue;
                        if (!pp[l])
                        {
                            dfs(l,1);
                        }
                    }
                    if (!flag)
                    {
                        fflag=1;
                        break;
                    }
                }
            if (fflag) printf("YES\n");
            else printf("NO\n");
        }
        else printf("YES\n");
    }
    return 0;
}

C zxa and UAV

Description

真是一个悲伤的故事,zxa的肾飞走了。取而代之,飞回来的是一个UAV(Unmanned Aerial Vehicle)。可恨的是UAV上面却写着sd0061!!!

后来才知道,sd0061就是zxa本人。

sd0061作为zxa的绰号相似度太低,让人非常难以辨认。所以,这并不是一个好绰号。

我们定义两个字符串的相似度是他们的最长公共前缀的长度。

现在给你 n 个人名字和n个绰号,请你选择一种搭配,使得总的相似度最大。

Input

输入包含多组测试数据,以EOF结束,不超过10组数据。

对于每组测试数据:

第一行包含一个正整数 n

接下来n行,每行包含一个字符串,表示一个人的名字。

接下来 n 行,每行包含一个字符串,表示一个待匹配的绰号。

1n500001n50000,每组数据的字符串总长度 106 ,字符串仅包含小写字母。

Output

对于每组测试数据输出一行,包含一个非负整数,表示搭配的最大相似度。

Sample Input

5
gennady
galya
boris
bill
toshik
bilbo
torin
gendalf
smaug
galadriel

Sample Output

11

题解

贪心,对两组串分别建立两颗trie树,然后记录trie上每个位置在串中出现的次数。然后求两个trie树对应点min值之和即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;

const int CHARSET=26,BASE='a',MAX_NODE=510000;
struct Trie
{
    int tot,root,child[MAX_NODE][CHARSET];
    int cnt[MAX_NODE];
    Trie()
    {
        memset(child[1],0,sizeof(child[1]));
        memset(cnt,0,sizeof(cnt));
        root=tot=1;
    }
    void clear()
    {
        memset(child[1],0,sizeof(child[1]));
        memset(cnt,0,sizeof(cnt));
        root=tot=1;
    }
    void insert(const char *str)
    {
        int *cur=&root;
        for (const char *p=str;*p;++p)
        {
            cur=&child[*cur][*p-BASE];
            if (*cur==0)
            {
                *cur=++tot;
                memset(child[tot],0,sizeof(child[tot]));
            }
            cnt[*cur]++;
        }
    }
}S,T;
int n;
char s[1100000];
int ans;
void dfs(int x,int y)
{
    ans+=min(S.cnt[x],T.cnt[y]);
    for (int i=0;i<26;i++)
    {
        if (S.cnt[S.child[x][i]]!=0&&T.cnt[T.child[y][i]]!=0) dfs(S.child[x][i],T.child[y][i]);
    }
}
int main()
{
    while (scanf("%d",&n)==1)
    {
        S.clear();
        T.clear();
        for (int i=1;i<=n;i++)
        {
            scanf("%s",s);
            S.insert(s);
        }
        for (int i=1;i<=n;i++)
        {
            scanf("%s",s);
            T.insert(s);
        }
        ans=0;
        dfs(1,1);
        printf("%d\n",ans);
    }
    return 0;
}

D zxa and tree

Description

zxa来到了一棵树面前,他觉得这棵树太单调了,所以决定给树染上黑白两种颜色。

树有 n 个节点,从1 n 标号,标号为ii的节点染白色的能获得Ai的愉悦值,染黑色能获得 Bi 的愉悦值,标号为 i 的白点和标号为j的黑点产生的额外愉悦值为 dist(i,j) ,每个节点只能恰好染一种颜色。

现在zxa想知道,对于每个非负整数 t(0tn) t,染恰好 t 个白点能获得的最大愉悦值是多少。

Input

输入包含多组测试数据,以EOF结束,不超过10组数据。

对于每组测试数据:

第一行包含一个正整数n

接下来 (n1) 行,每行包含两个正整数 x y,代表一条树边连接表标号为 x 和标号为y的点。

接下来一行包含 n 个非负整数,代表Ai(1in)

接下来一行包含 n 个非负整数,代表Bi(1in)

1n100,1x,yn,0Ai,Bi100

Output

对于每组测试数据输出一行,包含 (n+1) 个非负整数,其中第ii个数代表染 (i1) 个白点能获得的最大愉悦度,相邻的数字之间用恰好一个空格隔开。

Sample Input

3
1 2
2 3
1 1 1
2 2 2
10
8 1
1 9
9 7
1 2
7 10
8 5
7 3
2 6
8 4
10 2 7 2 9 2 6 1 2 10
10 4 8 9 5 5 10 3 2 10

Sample Output

6 8 7 3
66 99 123 138 147 146 142 133 113 87 51

题解

树上dp,首先我们需要枚举总共的白点个数。然后在树上进行dp。记录状态为 f[i][j] ,表示以 i 为根节点,有j个节点染为白色所能获得的最大的愉悦度。这样我们能很容易维护处染色部分贡献。现在问题主要在于如何求 dist(i,j) 这部分对答案的贡献。因为我们枚举了染的白点的个数,对于一个确定染白点个数的子树,子树与其父亲节点这条边出现在所有 dist(i,j) 中的次数就可以确定了,其等于子树内白点个数乘以子树外黑点的个数加子树内黑点的个数乘以子树外白点的个数。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>

using namespace std;
int n;
int a[110],b[110];
int e[210],pre[210],last[110],pp[110];
int f[110][110];
int g[110][110];
int nu[110];
int tot;
void work(int x)
{
    pp[x]=1;
    for (int i=last[x];i;i=pre[i])
    {
        if (!pp[e[i]])
        {
            work(e[i]);
            for (int j=0;j<=min(nu[x]+nu[e[i]],tot);j++)
                g[x][j]=0;
            for (int j=0;j<=nu[x];j++)
                for (int k=0;k<=min(nu[e[i]],max(0,tot-j));k++)
                {
                    g[x][j+k]=max(g[x][j+k],f[x][j]+f[e[i]][k]);
                }
            for (int j=0;j<=min(nu[x]+nu[e[i]],tot);j++)
                f[x][j]=g[x][j];
            nu[x]+=nu[e[i]];
        }
    }
    nu[x]++; 
    for (int j=0;j<=min(nu[x],tot);j++)
    {
        g[x][j]=0;
        if (j!=0) g[x][j]=max(g[x][j],f[x][j-1]+a[x]);
        if (j!=nu[x]) g[x][j]=max(g[x][j],f[x][j]+b[x]);
    }
    for (int i=0;i<=min(nu[x],tot);i++)
        f[x][i]=g[x][i]+i*((n-tot)-(nu[x]-i))+(nu[x]-i)*(tot-i);
}   
int main()
{
    while (scanf("%d",&n)==1)
    {
        memset(last,0,sizeof(last));
        memset(nu,0,sizeof(nu));
        for (int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            e[i*2]=y;
            pre[i*2]=last[x];
            last[x]=i*2;
            e[i*2-1]=x;
            pre[i*2-1]=last[y];
            last[y]=i*2-1;
        }
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for (int i=1;i<=n;i++)
            scanf("%d",&b[i]);
        for (int i=0;i<=n;i++)
        {
            memset(pp,0,sizeof(pp));
            memset(f,0,sizeof(f));
            memset(nu,0,sizeof(nu));
            tot=i;
            work(1);
            printf("%d",f[1][i]);
            if (i==n) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}

E zxa and path

Description

zxa有一个无向图,含有 n 个点和m条边,其中 n 个点从1 n 编号,m条边从 1 m编号,每条边都有一个正整数长度,一条路径的长度等于路径上边的长度之和。

zxa对单源最短路径产生了兴趣,所以他算出了从点 1 到任意一点i(i>1)的一条最短路径,并把这条路径上的边标记,所有被标记的边恰好组成了一棵生成树。

zxa很好奇,对于每一个点 i(i>1) ,如果将生成树上点 1 到点i的路径上与点 i 相连的那条边从无向图中删去,那么在新的无向图中点1到点 i 的最短路径长度是多少。

你能帮助他算出i=2,3,,n时的答案吗?

Input

输入包含多组测试数据,以EOF结束,不超过20组数据。

对于每组测试数据:

第一行包含两个正整数 n m,表示无向图的点数和边数。

接下来 m 行,第i行包含四个整数 ui,vi wi ,表示第 i 条边连接ui vi ,且长度为 wi

接下来一行包含 (n1) 个正整数,表示被标记的边的编号。

对于任意的一点 i(i>1) ,保证恰好存在一条从点 1 到点i的最短路径,使得路径上的每条边都是被标记的。

2n4000,n1m105,1ui,vin,1wi105

Output

对于每组测试数据输出一行,包含 (n1) 个整数,其中第 i 个数表示删去一条边后从点1到点 (i+1) 的最短路径长度,如果这样的路径不存在则长度视为 1 ,相邻数字之间用恰好一个空格隔开。

Sample Input

2 1
1 2 1
1
4 5
1 2 2
1 3 2
2 3 1
2 4 3
3 4 4
1 2 4
5 9
1 2 6
1 3 3
1 4 2
2 3 2
2 3 4
2 5 3
3 5 1
3 5 2
4 5 4
2 3 4 7

Sample Output

-1
3 3 6
6 7 8 5

题解

首先建立最短路径树,求出 1 点到每个其他节点的距离dist[i]考虑边权为 w 的非树边<u,v>,设 u,v 的最近公共祖先为 p ,那么<u,p>上的任意一点 i ,若断掉其连在树上的边,那么借由<u,v>使得 1 点到i点的最短路径变为 dist[u]+dist[v]+wdist[i] 。所以我们将每条边的 dist[u]+dist[v]+w 排序,因为每个只用被更新一次,我们利用并查集维护需要更新的下一个点即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
const int INF=1e9;
int pp[4100];
int x[110000],y[110000],w[110000];
int e[210000],last[4100],cost[210000],pre[210000],use[210000];
int cx[110000];
int fa[210000];
int dis[110000],sh[110000];
int n,m;
int nnum;
int rm[310000];
int dd[110000];
int father[110000];
void dfs(int x,int sum,int depth)
{
    nnum++;
    pp[x]=1;
    cx[x]=nnum;
    dis[x]=sum;
    sh[x]=depth;
    rm[nnum]=x; 
    for (int i=last[x];i;i=pre[i])
    {
        if (!pp[e[i]]) 
        {
            fa[e[i]]=x;
            dfs(e[i],sum+cost[i],depth+1);
            nnum++;
            rm[nnum]=x;
        }
    }
}
struct edge
{
    int u,v,w;
}a[110000];
int cmp(edge a,edge b)
{
    return a.w<b.w;
}
int num;
const int MAX=110000;
int stTable[MAX][32];
int preLog2[MAX];

void st_prepare(int n,int *array)
{
    preLog2[1]=0;
    for (int i=2;i<=n;i++)
    {
        preLog2[i]=preLog2[i-1];
        if ((1<<preLog2[i]+1)==i)
        {
            preLog2[i]++;
        }
    }
    for (int i=n-1;i>=0;i--)
    {
        stTable[i][0]=array[i];
        for (int j=1;(i+(1<<j)-1)<n;j++)
        {
            if (sh[stTable[i][j-1]]<sh[stTable[i+(1<<j-1)][j-1]]) stTable[i][j]= stTable[i][j-1];
            else stTable[i][j]= stTable[i+(1<<j-1)][j-1];
        }
    }
}
int query_min(int l,int r)
{
    if (l>r) swap(l,r);
    int len=r-l+1,k=preLog2[len];
    if (sh[stTable[l][k]]<sh[stTable[r-(1<<k)+1][k]]) return stTable[l][k];
    else return  stTable[r-(1<<k)+1][k];
}
int lca(int x,int y)
{
    return query_min(cx[x],cx[y]);
}
int getfather(int x)
{
    if (father[x]==x) return x;
    else father[x]=getfather(father[x]);
    return father[x];
}
int main()
{
    while (scanf("%d %d",&n,&m)==2)
    {
        memset(last,0,sizeof(last));
        memset(use,0,sizeof(use));
        for (int i=1;i<=m;i++)
        {
            scanf("%d %d %d",&x[i],&y[i],&w[i]);
        }
        for (int i=1;i<=n-1;i++)
        {
            int xx;
            scanf("%d",&xx);
            use[xx]=1;
            e[i*2]=y[xx];
            pre[i*2]=last[x[xx]];
            last[x[xx]]=i*2;
            e[i*2-1]=x[xx];
            pre[i*2-1]=last[y[xx]];
            last[y[xx]]=i*2-1;
            cost[2*i]=cost[2*i-1]=w[xx];
        }
        nnum=0;
        memset(pp,0,sizeof(pp));
        dfs(1,0,1);
        st_prepare(nnum,rm);
        num=0;
        for (int i=1;i<=m;i++)
        {
            if (!use[i])
            {
                num++;
                a[num].u=x[i],a[num].v=y[i],a[num].w=dis[x[i]]+dis[y[i]]+w[i];
            }
        }
        for (int i=2;i<=n;i++)
            dd[i]=INF;

        sort(a+1,a+1+num,cmp);
        for (int i=1;i<=n;i++)
            father[i]=i;
        for (int i=1;i<=num;i++)
        {
            int p=lca(a[i].u,a[i].v); 
            int f=getfather(a[i].u);        
            while (sh[f]>sh[p])
            {
                father[f]=getfather(fa[f]);
                dd[f]=a[i].w-dis[f];
                f=father[f];
            }
            f=getfather(a[i].v);        
            while (sh[f]>sh[p])
            {
                father[f]=getfather(fa[f]);
                dd[f]=a[i].w-dis[f];
                f=father[f];
            }
        }
        for (int i=2;i<=n;i++)
        {
            if (dd[i]==INF) printf("-1");
            else printf("%d",dd[i]);
            if (i!=n) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}

F zxa and xor

Description

zxa有一个长度为 n 01序列。定义 f(l,r) 为序列第 l 位到序列第r位的数字异或和。有三种操作:

0LR 询问有多少个 l,r 满足 LlrR ,使得 f(l,r)=0
1LR 询问有多少个 l,r 满足 LlrR ,使得 f(l,r)=1
2x 将位置 x 的数取反。
其中1LRn,1xn

你需要给出对序列进行 q 次上述的操作的结果。

Input

zxa有一个长度为n 0 1序列。定义 f(l,r) 为序列第 l 位到序列第r位的数字异或和。有三种操作:

0LR 询问有多少个 l,r 满足 LlrR ,使得 f(l,r)=0
1LR 询问有多少个 l,r 满足 LlrR ,使得 f(l,r)=1
2x 将位置 x 的数取反。
其中1LRn,1xn

你需要给出对序列进行 q 次上述的操作的结果。

Output

对于每组测试数据输出多行,其中对于每一种询问操作输出一行,包含一个非负整数,表示相应的答案。

Sample Input

1
10
1 0 0 1 0 0 1 0 1 1
4
0 1 3
1 2 5
2 3
1 2 5

Sample Output

3
6
4

题解

求出前缀和,用线段树维护区间内有多少前缀和为0,多少前缀和为 1 2号操作相当于将一个区间 0 的个数和1的个数交换。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;
int a[210000];
struct Seg
{
       struct data
       {
              int l,r,lazy;
              int n0,n1;
       }tr[2100000];
       void update(int k)
       {
                pushdown(k<<1);
                pushdown(k<<1|1);
                tr[k].n0=tr[k<<1].n0+tr[k<<1|1].n0;
                tr[k].n1=tr[k<<1].n1+tr[k<<1|1].n1;
       }
       void pushdown(int k)
       {
            if (tr[k].lazy)
            {
                swap(tr[k].n0,tr[k].n1);
                if (tr[k].l!=tr[k].r) tr[k<<1].lazy^=1,tr[k<<1|1].lazy^=1;
                tr[k].lazy=0;
            }
       }
       void build(int k,int l,int r)
       {
            tr[k].l=l,tr[k].r=r;
            tr[k].lazy=0;
            if (l==r)
            {
                if (a[l]==0) tr[k].n0=1,tr[k].n1=0;
                else tr[k].n1=1,tr[k].n0=0;
                return ;
            }
            int mid=(l+r)>>1;
            build(k<<1,l,mid);
            build(k<<1|1,mid+1,r);
            update(k);
       }
       void lchange(int k,int a,int b)
       {
            int l=tr[k].l,r=tr[k].r;
            pushdown(k);
            if (a==l&&b==r)
            {
                tr[k].lazy^=1;
                pushdown(k);
                return ;
            }

            int mid=(l+r)>>1;
            if (b<=mid) lchange(k<<1,a,b);
            else if (a>mid) lchange(k<<1|1,a,b);
            else lchange(k<<1,a,mid),lchange(k<<1|1,mid+1,b);
            update(k);
       }
       int lask(int k,int a,int b,int c)
       {
           pushdown(k);
           int l=tr[k].l,r=tr[k].r;
           if (a==l&&b==r)
           {
               if (c==0) return tr[k].n0;
               else return tr[k].n1;
           }
           int mid=(l+r)>>1;
           int tmp;
           if (b<=mid) tmp=lask(k<<1,a,b,c);
           else if (a>mid) tmp=lask(k<<1|1,a,b,c);
           else tmp=lask(k<<1,a,mid,c)+lask(k<<1|1,mid+1,b,c);
           update(k);
           return tmp;
       }
}T;
int q;    
LL ans;
int n;
int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
          scanf("%d",&n);
          for (int i=2;i<=n+1;i++)
              scanf("%d",&a[i]),a[i]^=a[i-1];
          n++;
          T.build(1,1,n);
          scanf("%d",&q);
          for (int i=1;i<=q;i++)
          {
              int op,x,y;
              scanf("%d",&op);
              if (op==0)
              {
                  scanf("%d %d",&x,&y);
                  x++,y++;
                  LL cf=0;
                  int l0=T.lask(1,x-1,y-1,0),l1,r0=T.lask(1,x,y,0),r1;
                  l1=(y-x+1-l0);
                  r1=(y-x+1-r0);
                  ans=(LL)l0*(LL)r0+(LL)l1*(LL)r1;
                  if (x!=y)
                  {
                    int xx=T.lask(1,x,y-1,0),yy;
                    yy=(y-x-xx);
                    cf=((LL)xx*(LL)xx+(LL)yy*(LL)yy+(LL)y-(LL)x)/2LL;
                    }
                  ans-=cf;
                  printf("%lld\n",ans);
              }
              else if (op==1)
              {
                  scanf("%d %d",&x,&y);
                  x++,y++;
                  LL cf=0;
                  int l0=T.lask(1,x-1,y-1,0),l1,r0=T.lask(1,x,y,0),r1;
                  l1=(y-x+1-l0);
                  r1=(y-x+1-r0);
                  ans=(LL)l0*(LL)r1+(LL)l1*(LL)r0;
                  if (x!=y)
                  {
                   int xx=T.lask(1,x,y-1,0),yy;
                   yy=(y-x-xx);
                    cf=((LL)xx*(LL)yy+(LL)xx*(LL)yy)/2LL;
                   }
                  ans-=cf;
                  printf("%lld\n",ans);
              }
              else 
              {
                   scanf("%d",&x);
                   x++;
                   T.lchange(1,x,n);
              }
          }
    }
    return 0;
}

G zxa and robot

Description

zxa在二维平面上放置了一个机器人,初始坐标为 (0,0) ,机器人第 i 步可以在上下左右四个方向中挑选一个方向走3i1步。

现在zxa想要让机器人走到 (x,y) ,问至少走多少步可以到达目标点。

Input

输入包含多组测试数据,以EOF结束,不超过10组数据。

每组测试数据仅占一行,包含两个整数 x y

0|x|,|y|1018

Output

对于每组测试数据输出一行,包含一个整数,代表到达目标点至少需要的步数,如果不存在到 (x,y) 的方案则输出 1

Sample Input

8 3
1 1

Sample Output

3
-1

Hint

对于第一个样例,一种可行的最少步数的方案 是 (0,0)(1,0)(1,3)(8,3)

题解

将x和y取绝对值后转化为 3 进制,贪心,如果该位是1,则正向走,如果该位是 2 ,则负向走且给下一位加1。如果两个数有一位同时不为0,或者该位都为0,且此时未到终点,则无解。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;
LL a[100],b[100];
int pp[100];
int n1,n2;
LL x,y;
int main()
{
    while (cin>>x>>y)
    {
          x=abs(x),y=abs(y);
          memset(pp,0,sizeof(pp));
          memset(a,0,sizeof(a));
          memset(b,0,sizeof(b));
          int num=0;
          n1=n2=0;
          while (x>0)
          {
              n1++;
              a[n1]=x%3;
              x/=3;
          }
          while (y>0)
          {
              n2++;
              b[n2]=y%3;
              y/=3;
          }
          int flag=0;
          int pos;
          int tt=0;
          for (int i=1;;i++)
          {
              if (pp[i]) continue;
              if (a[i]*b[i]!=0)
              {
                  tt=1;
                  break;
              }
              if (a[i]+b[i]==0)
              {
                  flag=1;
                  pos=i;
                  break;
              }
              if (a[i]==1)
              {
                  pp[i]=1;
              }
              else if (a[i]==2)
              {
                   pp[i]=1;
                   a[i+1]++;
                   int d=i+1;
                   while (a[d]==3)
                   {
                       a[d+1]++;
                       a[d]=0;
                       d++;
                   }
              }
              else if (b[i]==1)
              {
                   pp[i]=1;
              }
              else 
              {
                   pp[i]=1;
                   b[i+1]++;
                   int d=i+1;
                   while (b[d]==3)
                   {
                       b[d+1]++;
                       b[d]=0;
                       d++;
                   }
              }
          }
          if (tt)
          {
                 printf("-1\n");
                 continue;
          }
          if (flag)
          {
              int fflag=0;
              for (int i=pos;i<100;i++)
              {
                  if (a[i]+b[i]!=0)
                  {
                      fflag=1;
                      break;
                  }
              }
              if (fflag) printf("-1\n");
              else printf("%d\n",pos-1);
          }
          else printf("%d\n",pos-1);
    }          
    return 0;
}

H zxa and array

Description

zxa有一个数组 a=[a1,a2,,an] ,但是 a 上面的数字已经模糊不清了。

幸运的是,zxa知道ai(1in)的取值范围为 1 mi之间的所有正整数组成的集合,并且数组中每个数字都互不相同。

问有多少个不同的数组 a 满足条件,答案对(109+7)取模。

Input

输入包含多组测试数据,以EOF结束,不超过10组数据。

对于每组测试数据:

第一行包含一个正整数 n

第二行包含n个正整数,表示 mi(1in)

1n1000,1mi106

Output

对于每组测试数据输出一行,包含一个非负整数,表示答案对 (109+7) 取模的值。

Sample Input

3
4 2 2
3
2 2 2

Sample Ouput

4
0

Hint

对于第一个样例,可能的aa有 [3,1,2] [3,2,1] [4,1,2] [4,2,1]

题解

mi 排成升序重新标号,那么之前选过的数后面的不能再选,所以答案为

(mii+1)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;
const LL mod=1e9+7;
LL a[1100];
int n;
int main()
{
    while (cin>>n)
    {
        for (int i=1;i<=n;i++)
            cin>>a[i];
        sort(a+1,a+1+n);
        LL ans=a[1];
        for (int i=2;i<=n;i++)
            ans=(ans*max(a[i]-i+1,0LL))%mod;
        cout<<ans<<endl;
    }
    return 0;
}

I zxa and jing’a

Description

zxa有一个只包含 0,1 的序列,你可以对这个序列进行两种操作:

花费 A 的代价把一段区间内的数字排逆序,也就是1在排好的前面, 0 在后面。
花费 1 的代价交换任意两个数。
每种操作可以使用无限次,求把这个序列排成非降序列的最小花费。

Input

输入包含多组测试数据,以EOF为结尾,不超过80组数据。

对于每组测试数据:

第一行包含两个非负整数 n A ,分别表示序列的长度和每次使用第一种操作的代价。

第二行包含 n 个非负整数 ai(1in) , 表示zxa的序列。

1n105,0A109,0ai1

Output

对于每组测试数据输出一行,包含一个非负整数,表示将这个序列排序的最小花费。

Sample Input

3 1
1 1 1
4 0
1 1 0 1
8 1
1 1 0 1 0 1 0 0

Sample Output

0
1
3

Hint

劲啊!

题解

很容易发现,排序操作是没有用的。所以直接统计有多少个不在正确位置上的0即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
int n,A;
int a[110000];
int num,ans;
int main()
{
    while (scanf("%d %d",&n,&A)==2)
    {
          num=ans=0;
          for (int i=1;i<=n;i++)
          {
              scanf("%d",&a[i]);
              if (a[i]==0) num++;
          }
          for (int i=1;i<=n;i++)
          {
              if (a[i]==0&&i>num) ans++;
          }
          printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值