第一题:信(believe.cpp/c/pas)
背景描述:
一切死亡都有冗长的回声
—— 《一切》北岛
给定一个N个元素的序列A,
定义Bi = (Ai and A1) + (Ai and A2) + (Ai and A3)+ ...... + (Ai and An)
定义Ci = (Ai or A1) + (Ai or A2) + ... + (Ai or An)
求B和C序列。
输入格式:
第一行一个整数N表示序列长度
接下来一行N个整数, 第i个整数为Ai
输出格式:
第一行N个整数输出B序列
第二行N个整数输出C序列
样例输入:
4
3 5 1 1
样例输出:
6 8 4 4
16 22 10 10
数据规模:
对于50%的数据, N <= 1000
对于100%的数据, N <= 100000, Ai <= 100000
解:做这道题要明白两点:1、每次都有A1~An,所以要考虑整体
2、因为是求和,所以与相应的每个Ai无关。
所以 记录一个sum数组表示每一位A[]中为1的个数 。
& 的情况:Ai当前位为1,那么b[i]为sum[当前位]*(1<<当前位)
| 的情况:Ai当前位idx为1,c[i]+=n*(1<<(idx-1));当前位为0,则c[i]+=sum[j]*(1<<(j-1));
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 100005 #define ll long long #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; ll n; ll a[maxn]; ll b[maxn],c[maxn]; ll sum[20]; int main() { freopen("beleive.in","r",stdin); freopen("believe.out","w",stdout); scanf(AUTO,&n); for (int i=1;i<=n;i++) { scanf(AUTO,&a[i]); ll x=a[i]; int idx=0; while (x>0) { idx++; if ((1&x)) sum[idx]++; x>>=1; } } int id=1; while (sum[id]) id++; for (int i=1;i<=n;i++) { ll x=a[i]; int idx=0; while (x>0) { idx++; if ((1&x)) { b[i]+=sum[idx]*(1<<(idx-1)); c[i]+=n*(1<<(idx-1)); } else { c[i]+=sum[idx]*(1<<(idx-1)); } x>>=1; } for (int j=idx+1;j<=id;j++) c[i]+=sum[j]*(1<<(j-1));//***** } for (int i=1;i<=n;i++) printf(AUTO" ",b[i]); printf("\n"); for (int i=1;i<=n;i++) printf(AUTO" ",c[i]); return 0; }
第二题:心(heart.cpp/c/pas)
背景描述:
不是一切深渊都是灭亡
不是一切灭亡都覆盖在弱者的头上
——《这也是一切》 舒婷
有N个透明的盒子, 每个盒子里面有两个不同颜色的球, 总共有M种颜色。
Alice和Bob又在玩游戏, 具体的, Alice会从N个盒子里面选出若干个, Bob再从Alice选出的盒子里面选出一些(不能不选), 如果在Bob选出的盒子中, 每 个颜色的球都总共出现了偶数次(0次也是偶数次), 那么Bob胜利, 否则Alice胜利
在Alice和Bob都足够聪明的情况下, Alice想知道自己在能够获胜的前提下, 第一次最多可以选出几个盒子
输入格式:
第一行有两个整数N和M, 意义如题
接下来N行, 第i+1行两个整数表示第i+1个盒子里面的两个球分别是什么颜色的
输出格式:
一行一个整数表示Alice最多可以选多少个盒子
样例输入:
3 3
1 2
2 3
2 3
样例输出:
2
数据规模: 对于30%的数据, N <= 10
对于50%的数据, N <= 20
对于100%的数据, N <= 100000, M <= 2N
解: 一眼容易想成其他的算法。画一个图,以颜色为节点,盒子为边,那么边不能重复,不能有环。所以用并查集。考试的时候写了一个kruskal....结果发现思路都错了。。。。
注意:奇数个相同的盒子也不行,因为B可以只取其中的偶数个,就算你现在选了奇数个,也无法逃过B 的聪明智慧。。。。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 100005 using namespace std; int n,m,fa[maxn<<1],ans;//开两倍 int find(int x) { if (fa[x]!=x) return fa[x]=find(fa[x]); return fa[x]; } int main() { freopen("heart.in","r",stdin); freopen("haert.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) fa[i]=i; for (int i=1;i<=n;i++) { int x,y; scanf("%d%d",&x,&y); int r1=find(x); int r2=find(y); if (r1!=r2) { ans++; fa[r2]=r1; } } printf("%d",ans); return 0; }
第三题:题(problem.cpp/c/pas)
背景描述:
黑夜给了我黑色的眼睛
我却用它寻找光明
——《一代人》 顾城
已知一个N个元素的非负整数序列A,
定义Bi = (Ai and A1) + (Ai and A2) + (Ai and A3)+ ...... + (Ai and An)
定义Ci = (Ai or A1) + (Ai or A2) + ... + (Ai or An)
给出B和C序列, 构造一个满足条件的A序列, 如果没有, 输出-1
输入格式:
第一行一个整数N。
接下来一行N个整数表示B序列
接下来一行N个整数表示C序列
输出格式:
如果有解, 输出一行N个整数表示你构造的A序列, SPJ很脆弱, 所以你构造的序列每个整数必须在[0,8191]这个区间内, 我们保证如果有解, 那么一定存在一个解满足每个元素均在[0,8191]内
否则输出-1
样例输入:
4
6 8 4 4
16 22 10 10
样例输出:
3 5 1 1
数据规模:
对于30%的数据, N = 2
对于70%的数据, N <= 200
对于100%的数据, N <= 100000, Bi , Ci<= 1000000000
解:注意一点:& 取的是每一位为1 的交集,| 取的是每一位为 1 的并集。。
所以A&B+A|B=A+B
所以Ci+Bi=nAi+sum (sum:Ai之和)
∑(Ci+Bi)=n*sum(Ai相加)+n*sum (n项B[],C[])
∴sum=∑/(2*n)
∴就知道Ai=(Ci+Bi-sum)/n
然后如果不能整除,则输出-1,如果Ai大于INF,则输出-1
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100005 #define INF 8191 #define ll long long #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; ll n,a[maxn],b[maxn],c[maxn],s,sum; int main() { freopen("problem.in","r",stdin); freopen("problem.out","w",stdout); scanf(AUTO,&n); for (int i=1;i<=n;i++){ scanf(AUTO,&b[i]); s+=b[i]; } for (int i=1;i<=n;i++){ scanf(AUTO,&c[i]); s+=c[i]; } sum=(s/n)>>1; if (2*n*sum!=s) {//******* printf("-1"); return 0; } for(int i=1;i<=n;i++) { ll si=b[i]+c[i]; a[i]=(si-sum)/n; if (a[i]>INF){ printf("-1"); return 0; } } for (int i=1;i<=n;i++) printf(AUTO" ",a[i]); return 0; }
吐槽:
这套题叫信心题。。。然后我GG了。
文件名有毒,输入文件和输出文件名不同,还好我眼尖,不然全班GG。
明天就考试,马上就出发,GGGGGGGGGGG