国庆 day 6 下午
1.数组异或
(xorarray.pas/c/cpp)
(xorarray.in/out)
时间限制:2s/空间限制:256M
【题目描述】
xor——异或,和 and 与or 一样,是一种重要的逻辑运算,他的运算规律是 0 xor 0 = 0,1 xor 1 = 0,1 xor 0 = 1,0 xor 1 = 1
两个整数之间的异或是将两个整数转化成二进制,对他们的每一位分别进行 xor 操作,例:6(110) xor 13(1101) = 11(1011)
现在我们要介绍一种新的操作——数组异或,将两个相同大小(假设都为n)的数组A、B异或成一个新数组C,则新数组必满足:
现在给你数组大小n,和两个数组A,B
求他们的异或数组C
由于最终答案可能过大,你需要对C的每个元素对109+7取模
【输入格式】(xorarray.in)
一共3行。
第一行一个正整数。
接下来两行每行个正整数,表示数组A、B。
【输出格式】(xorarray.out)
一共行,个正整数,表示数组C。
【输入输出样例1】
xorarray.in |
xorarray.out |
7 20670 1316 25227 8316 21095 28379 25235 19745 6535 14486 5460 15690 1796 12403 |
7583 52096 161325 276944 453024 675974 958287
|
【数据规模约定】
对于50%的数据,N≤100
对于全部的数据,N≤100000
思路:二进制拆分,维护一个二进制前缀和。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 100100 #define mod 1000000007 using namespace std; int n; int bit[31]; int a[MAXN],b[MAXN],c[MAXN]; int suma[MAXN][31],sumb[MAXN][31]; int finda(int r,int wh){ long long tot=0; for(int i=0;i<=30;i++) if(wh&bit[i]) tot=(tot+1ll*(r-suma[r][i])*bit[i])%mod; else tot=(tot+1ll*suma[r][i]*bit[i])%mod; return int(tot%mod); } int findb(int r,int wh){ long long tot=0; for(int i=0;i<=30;i++) if(wh&bit[i]) tot=(tot+1ll*(r-sumb[r][i])*bit[i])%mod; else tot=(tot+1ll*sumb[r][i]*bit[i])%mod; return int(tot%mod); } int main(){ freopen("xorarray.in","r",stdin); freopen("xorarray.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); int x=a[i],len=0; while(x){ suma[i][len++]=suma[i-1][len]+(x&1); x/=2; } for(int j=len;j<=30;j++) suma[i][j]=suma[i-1][j]; } for(int i=1;i<=n;i++){ scanf("%d",&b[i]); int x=b[i],len=0; while(x){ sumb[i][len++]=sumb[i-1][len]+(x&1); x/=2; } for(int j=len;j<=30;j++) sumb[i][j]=sumb[i-1][j]; } bit[0]=1; for(int i=1;i<=30;i++) bit[i]=bit[i-1]<<1; c[1]=a[1]^b[1]; for(int k=2;k<=n;k++){ c[k]=c[k-1]; c[k]=(c[k]+finda(k-1,b[k]))%mod; c[k]=(c[k]+findb(k,a[k]))%mod; } for(int i=1;i<=n;i++) cout<<c[i]<<" "; }
2.侦探游戏
(detective.pas/c/cpp)
(detective.in/out)
时间限制:1s/空间限制:256M
【题目描述】
小W最近沉迷一个侦探游戏,在这个游戏中会不断出现命案,而小W作为主角,需要不断地收集各种关键证据,只有当所有的关键证据都被找到,你才能 驳倒所有人错误的判断,找出真正的凶手。
一共有个关键证据以及条信息,每条信息如下所示 : 如果你已经掌握了证据 i ,那么你可以通过 k 个时间的搜索和推理得到证据 j ,同样的,如果你掌握了证据 j 你也可以通过 k 个时间得到证据 j 。
游戏开始时玩家通过初步观察现场已经得到了证据1,于此同时,每个玩家在游戏开始阶段时都能获得一个特殊技能来加快游戏进度,增加趣味性。小 W 选了一个他以前从来没用过的技能 : 好运。这是一个被动技能,系统会在游戏开始时选定一对证据(a,b)当小W发现其中一个证据的时候,他会很好运地立即获得另外一个证据(不计入时间)。
但是这个技能是完全随机的,小W完全不知道进入游戏后系统会挑选哪一对证据,他希望你能帮助他算出他花在本轮游戏上的时间的期望值,这样他心里能有点B数。
提供的信息保证 : i不会等于j,每个k值都互不相同,个证据都能被得到。
【输入格式】(detective.in)
一共行。
第一行两个正整数,表示证据数量和信息数量。
接下来行,每行三个数字i,j,k表示一个信息
【输出格式】(xorarray.out)
一共行,个整数(期望值是实数,但这里请直接保留0位小数输出)
【输入输出样例】
detective.in |
detective.out |
3 1 2 3 1 3 2 2 3 5 |
2.3333
|
【数据规模约定】
对于的数据,
对于的数据,
对于全部的数据,
【样例解释】
答案应该是2.33333......,取2
以下思路来自xxy大佬的博客。
60分做法:
先做一遍最小生成树
枚举两个点i,j,那么替换掉的是最小生成树上i,j路径上权值最大的边
倍增维护
时间复杂度:O(n*n*logn)
100分做法:
换一个角度,考虑被替换掉的边的贡献
边e要想被替换掉,那么点 i,j 要满足两个条件:
① 设e的两端点所连的子树为u,v,i∈u,j∈v
② e的边权是连通 i,j 必经之路(最短路)上边权最大的边
怎么找这条边?——Kruscal算法
Kruscal算法每次找还没有加进去的权值最小的边,所以满足必经之路
还没有加进去的边权最小的边,是已加进去的边权最大的边,所以满足路径上边权最大
所以,设最小生成树的总权值为sum,设当前边权为w,当前边连接的两点的集合大小分别为 s1、s2
ans=(sum*n*(n-1)- 2*Σ s1*s2*w)/(n*(n-1))
n*(n-1):任意选两个点共有这些选法
Σ 前乘2:u,v 和 v,u 算不同的方案
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 20010 #define N 100010 using namespace std; int n,m; long long sum,cnt; int fa[M],size[M]; struct nond{ int u,v,w; }edge[N]; int cmp(nond a,nond b){ return a.w<b.w; } int find(int k){ if(fa[k]==k) return k; return fa[k]=find(fa[k]); } int main(){ freopen("detective.in","r",stdin); freopen("detective.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); sort(edge+1,edge+1+m,cmp); for(int i=1;i<=n;i++) fa[i]=i,size[i]=1; for(int i=1;i<=m;i++){ int dx=find(edge[i].u); int dy=find(edge[i].v); if(dx==dy) continue; fa[dy]=dx; sum+=edge[i].w; cnt+=1ll*size[dx]*size[dy]*edge[i].w; size[dx]+=size[dy]; } printf("%.2lf",sum-cnt*2.0/(n*(n-1))); }
3.棋盘迷宫
(boardgame.pas/c/cpp)
(boardgame.in/out)
时间限制:5s/空间限制:256M
【题目描述】
小A和小Z是非常要好的朋友,而且他们都对迷宫游戏非常有兴趣。他们经常在自习课上用迷宫来打发时间(两位都是学习效率400%的dalao,大家切记不要模仿)。
他们的迷宫非常简单,又被他们叫做是棋盘迷宫,迷宫本身是一个N*M大小的棋盘,从左往右列数不断加大,从上往下行数不断增大,故左上角的坐标为(1, 1),右下角的坐标为(N,M)。当你处在坐标为(X,Y)的格子里时,你只能走到(X+1,Y)或(X,Y+1),即只能往右走或者往下走(当然你也不能走出棋盘)。同时部分格子中有障碍物,这样的格子是不能经过的,用‘#’代表,能正常通行的格子由‘.’代表。
每一轮游戏先由其中一人选定起点和终点,由另外一个人来完成)。但是他们很快发现,并不是每次都能找到一条从起点到达终点的路径,两位dalao的注意力立刻从游戏转移到了如何快速判断路径是否存在这个问题上。
当然dalao们瞬间得到了算法,不过他想考考你,如果你能顺利解决,也许就能得到他们两个的签名哦!(据说是最高级别的因果律护身符,能让人逢考必过)
【输入格式】(boardgame.in)
一共行。
第一行两个正整数为,表示棋盘迷宫的行列。
接下来行,每行一个长度为的字符串,由‘#’‘.’两种字符组成
接下来1行,一个正整数,表示询问的个数
接下来行,每行四个正整数x1,y1,x2,y2询问点(x1,y1)到(x2,y2)是否有一条合法的路径
数据保证(x1,y1)(x2,y2)所在的位置都是‘.’,同时x1<=x2,y1<=y2
【输出格式】(boardgame.out)
一共行,每行一个字符串或者,对应每一个询问的答案。
【输入输出样例】
boardgame.in |
boardgame.out |
4 4 .... ..#. .##. .... 3 2 2 4 4 2 1 4 4 1 2 4 4
|
No Yes Yes |
【数据规模约定】
对于40%的数据,N,M<=100,Q<=1000
对于全部的数据,N,M<=500,Q<=1000000
以下思路来自xxy大佬的博客。
分治+DP+bitset
若点(x1,y1)能访问到点(x2,y2)
假设x1<=x2,y1<=y2
那么(x1,y1)往右下扩展,(x2,y2)往左上扩展,两个点一定在中间某一行相遇
所以可以利用分治的思想枚举相遇的那一行
如果x1和x2都在枚举的这一行的上面,那就扔到左边
如果x1和x2都在枚举的这一行的下面,那就扔到右边
左边和右边的分别继续分治下去
如果x1和x2一个在上面,一个在下面,那就判断这两个点能否在这一行相遇
能在这一行相遇,两个点一定能相遇
反之,一定不能相遇
如何判断 ?
假设当前枚举的相遇行为mid,
f[i][j][k]=0/1表示第i行第j列的点,向右下扩展,能否扩展到第mid行的第k列
g[i][j][k]=0/1表示第i行第j列的点,向左上扩展,能否扩展到第mid行的第k列
若 f[x1][y1][k] 和 g[i][j][k] 有一个k同时为true,那么两个点就可以相遇
第三维可以用bitset简化
f的转移:
预处理:f[mid][j][j]=第mid行第j列是否有障碍
倒叙枚举mid行之上的i,j,f[i][j]|=f[i][j+1],f[i][j]|=f[i+1][j]
g的转移类似,
正序枚举mid行之下的,加号改成减号即可
#include<bitset> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 501 #define M 1000000 using namespace std; int n,m,Q; char s[MAXN]; bool map[MAXN][MAXN],ans[M]; bitset<MAXN>f[MAXN][MAXN],g[MAXN][MAXN]; struct nond{ int sx,sy,tx,ty,id; }p; void slove(vector<nond>vec,int l,int r){ if(l>r) return ; int mid=(l+r)/2; for(int i=mid;i>=l;i--) for(int j=m;j;j--){ f[i][j]=0; if(!map[i][j]) continue; if(i==mid) f[i][j].set(j); else f[i][j]|=f[i+1][j]; if(j!=m) f[i][j]|=f[i][j+1]; } for(int i=mid;i<=r;i++) for(int j=1;j<=m;j++){ g[i][j]=0; if(!map[i][j]) continue; if(i==mid) g[i][j].set(j); else g[i][j]|=g[i-1][j]; if(j!=1) g[i][j]|=g[i][j-1]; } vector<nond>vl,vr; for(vector<nond>::iterator it=vec.begin();it!=vec.end();it++){ p=*it; if(p.tx<mid) vl.push_back(p); else if(p.sx>mid) vr.push_back(p); else ans[p.id]=(f[p.sx][p.sy]&g[p.tx][p.ty]).any(); } slove(vl,l,mid-1); slove(vr,mid+1,r); } int main(){ freopen("boardgame.in","r",stdin); freopen("boardgame.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%s",&s); for(int j=0;j<m;j++) if(s[j]=='.') map[i][j+1]=1; else map[i][j+1]=0; } scanf("%d",&Q); vector<nond>vec; for(int i=1;i<=Q;i++){ scanf("%d%d%d%d",&p.sx,&p.sy,&p.tx,&p.ty); p.id=i; vec.push_back(p); } slove(vec,1,n); for(int i=1;i<=Q;i++) if(ans[i]) cout<<"Yes"<<endl; else cout<<"No"<<endl; } /* 4 4 .... ..#. .##. .... 3 2 2 4 4 2 1 4 4 1 2 4 4 */