MST
给定一张n个点的完全图,点的编号为0~n-1,每两个点u,v间的边权和为u xor v(xor为两个数的异或),求这个图的最小生成树所有边权的异或和。
【输入格式】
输入一行一个数n,
【输出格式】
输出一个数表示该图的最小生成树边权异或和。
【数据规模与约定】
对于1~4的测试点:n<=1000
对于5~6的测试点:n<=7000
对于7~8的测试点:n<=10^7
对于9~10的测试点:n<=10^12
考试的时候各种胡思乱想。。。这数据怕不是要log(n)???O(1)(下面有O(1) by 羊肉汤泡煎饼)。。。异或来异或去。。老脸MengB。。。
要不,打表。。。。开始打prim的表、、、、以下是d数组(就是把它们异或起来就是答案)
what。。。
what。。。woc!震惊。。竟然长得如此之像。。。于是想求出每个数的出现次数。。。如果是偶数就相当于没有贡献,是奇数就异或一次。。。log(n)妥妥的。。。
#include<cstdio> #include<iostream> #define ll long long #define R register ll using namespace std; ll n; ll pow[42]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648,4294967296,8589934592,17179869184,34359738368,68719476736,137438953472,274877906944,549755813888,1099511627776,2199023255552}; ll ans; signed main() { freopen("mst.in","r",stdin); freopen("mst.out","w",stdout); scanf("%lld",&n); //cout<<n<<endl; for(R i=1;pow[i]/2<=n;++i) { ans^=((((n-pow[i-1]+pow[i]-1)/pow[i])&1)?pow[i-1]:0); //cout<<pow[i-1]<<pow[i]<<" "<<((n-pow[i-1]+pow[i]-1)/pow[i])<<endl; } printf("%lld\n",ans); }
而旁边的 羊肉汤泡煎饼 发现了通项公式。。。太强了。。。十分接近O(1)。。。只不过说异或常数大。。。还是%%%
scanf("%lld",&n);n--; printf("%lld",(n^(n/2)));
震恐而瑟瑟发抖。。。
好吧,打表大法好。。。
2019.04.18