您的位置:首页 > 其它

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

2016-06-06 22:05 417 查看

Preface

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

A zxa and split

Description

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

每经过一年,一只zxa会分裂成两只,若它的坐标为x,则两只新的zxa分别产生在坐标为(x−1)和(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。

0≤n,T,|w|≤105,0≤|Xi|≤T,0<Ci≤105

Output

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

Sample Input

2 2 2
0 3
1 2


Sample Output

3


Hint

坐标第0年第1年第2年−2003−10320326123420233002

题解

很容易发现是组合数,预处理下阶乘和逆元的前缀和,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
*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到(n−1)编号,另给出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之间有矛盾。小朋友不会自己与自己有矛盾,但是同样的矛盾在描述中可能会给出多次。

1≤n≤200,0≤m≤600,0≤k≤8,0≤x,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行,每行包含一个字符串,表示一个待匹配的绰号。

1≤n≤500001≤n≤50000,每组数据的字符串总长度≤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(0≤t≤n)t,染恰好t个白点能获得的最大愉悦值是多少。

Input

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

对于每组测试数据:

第一行包含一个正整数n。

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

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

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

1≤n≤100,1≤x,y≤n,0≤Ai,Bi≤100

Output

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

Sample Input

31 2
2 31 1 1
2 2 2
10
8 1
1 9
9 7
1 2
7 10
8 5
7 32 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 366 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。

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

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

2≤n≤4000,n−1≤m≤105,1≤ui,vi≤n,1≤wi≤105

Output

对于每组测试数据输出一行,包含(n−1)个整数,其中第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 33 4 4
1 2 4
5 9
1 2 6
1 3 31 4 2
2 3 2
2 3 4
2 5 33 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]+w−dist[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的0、1序列。定义f(l,r)为序列第l位到序列第r位的数字异或和。有三种操作:

0LR 询问有多少个l,r满足L≤l≤r≤R,使得f(l,r)=0。

1LR 询问有多少个l,r满足L≤l≤r≤R,使得f(l,r)=1。

2x 将位置x的数取反。

其中1≤L≤R≤n,1≤x≤n。

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

Input

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

0LR询问有多少个l,r满足L≤l≤r≤R,使得f(l,r)=0。

1LR询问有多少个l,r满足L≤l≤r≤R,使得f(l,r)=1。

2x将位置x的数取反。

其中1≤L≤R≤n,1≤x≤n。

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

Output

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

Sample Input

1
10
1 0 0 1 0 0 1 0 1 1
4
0 1 31 2 5
2 31 2 5


Sample Output

36
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步可以在上下左右四个方向中挑选一个方向走3i−1步。

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

Input

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

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

0≤|x|,|y|≤1018。

Output

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

Sample Input

8 31 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(1≤i≤n)的取值范围为1与mi之间的所有正整数组成的集合,并且数组中每个数字都互不相同。

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

Input

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

对于每组测试数据:

第一行包含一个正整数n。

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

1≤n≤1000,1≤mi≤106

Output

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

Sample Input

34 2 2
32 2 2


Sample Ouput

4
0


Hint

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

题解

将mi排成升序重新标号,那么之前选过的数后面的不能再选,所以答案为∏(mi−i+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(1≤i≤n) , 表示zxa的序列。

1≤n≤105,0≤A≤109,0≤ai≤1

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: