20180516小测
Posted Cmd2001
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20180516小测相关的知识,希望对你有一定的参考价值。
T1:
我们能够证明(显然)这样的一个网络能输出任何[0,2^n-1]的排列(可以用归纳法证明),于是-1是不存在的了。
考虑每个节点只有两种状态,能否2-sat做呢?似乎有一些节点的状态是存在依赖关系的,然而并不会建图。
(于是我就写了20分暴力,先枚举那些节点激活然后进行大模拟)
正解是考虑这个网络的形态,观察可得一个节点输出的两个信号会被分配到左右的两个子网络中。
如果我们想构造出解的话,需要让第一次每个节点的两个信号进入两个不同的子网络,最后一层每个节点的两个信号从两个不同的子网络中得来。这个就是依赖关系了。
等等,这个似乎不需要2-sat,因为要字典序最小,我们能钦定第一行第一个节点状态为0,然后通过数值关系推出每个数字必须在哪个子网络中,这样与1号节点存在依赖关系的节点的状态也就都确定了。
然后继续扫描第一行,如果存在某个节点的状态没有被确定,那么让他为0与前面最优化的答案一定不冲突,我们就让他为0,然后继续递推就好了。
然后完成某一级的答案求解后,递归求出两个子网络的状态即可。
复杂度O(n*2^n)。
20分暴力代码:
1 #include<cstdio> 2 #include<algorithm> 3 const int maxn=11; 4 const int inf=0x3f3f3f3f; 5 6 bool sta[maxn][maxn]; 7 int in[maxn][maxn],tar[maxn],Log[maxn]; 8 9 inline int shl(int x,int l) { 10 int ret = 0; 11 for(int i=0;i<l;i++) if( x & ( 1 << i ) ) ret |= 1 << ( ( i - 1 + l ) % l ); 12 return ret; 13 } 14 inline int shr(int x,int l) { 15 int ret = 0; 16 for(int i=0;i<l;i++) if( x & ( 1 << i ) ) ret |= 1 << ( ( i + 1 ) % l ); 17 return ret; 18 } 19 20 inline void solve(int x,int y,int z,int inlev,int outlev,int n) { // doubled data level . 21 if( n == 1 ) return; 22 for(int i=0;i<n>>1;i++) { // cross node from inlev to inlev + 1 . 23 int l = y + i * 2 , r = y + i * 2 + 1; 24 in[inlev+1][l] = in[inlev][l] , in[inlev+1][r] = in[inlev][r]; 25 if( sta[x][(y>>1)+i] ) std::swap(in[inlev+1][l],in[inlev+1][r]); 26 } 27 if( n != 2 ) { 28 for(int i=0;i<n>>1;i++) { // cross line from inlev + 1 to inlev + 2 . 29 int ix = y + i * 2 , iy = y + i * 2 + 1 , ox = y + shl(i*2,Log[n]) , oy = y + shl(i*2+1,Log[n]); 30 in[inlev+2][ox] = in[inlev+1][ix] , in[inlev+2][oy] = in[inlev+1][iy]; 31 } 32 solve(x+1,y,z-1,inlev+2,outlev-2,n>>1) , solve(x+1,y+(n>>1),z-1,inlev+2,outlev-2,n>>1); 33 for(int i=0;i<n>>1;i++) { // cross line from outlev - 1 to outlev . 34 int ix = y + i * 2 , iy = y + i * 2 + 1 , ox = y + shr(i*2,Log[n]) , oy = y + shr(i*2+1,Log[n]); 35 in[outlev][ox] = in[outlev-1][ix] , in[outlev][oy] = in[outlev-1][iy]; 36 } 37 } 38 for(int i=0;i<n>>1;i++) { // cross node from outlev to outlev + 1. 39 int l = y + i * 2 , r = y + i * 2 + 1; 40 in[outlev+1][l] = in[outlev][l] , in[outlev+1][r] = in[outlev][r]; 41 if( sta[z][(y>>1)+i] ) std::swap(in[outlev+1][l],in[outlev+1][r]); 42 } 43 } 44 45 inline bool dif(int n,int n_lev) { 46 for(int i=0;i<1<<n;i++) if( in[n_lev][i] != tar[i] ) return 1; 47 return 0; 48 } 49 inline void unzipsta(int ss,int x,int y) { 50 for(int i=x-1;~i;i--) for(int j=y-1;~j;j--) sta[i][j] = ss & 1 , ss >>= 1; 51 } 52 53 int main() 54 static int sol,n,full,n_nod,m_nod,n_lev; 55 while( scanf("%d",&n) == 1 && n ) { 56 sol = 0 , n_nod = 2 * n - 1 , m_nod = 1 << ( n - 1 ) , n_lev = 2 * n_nod - 1 , full = 1 << ( n_nod * m_nod ); 57 for(int i=0;i<=n;i++) Log[1<<i] = i; 58 for(int i=0;i<1<<n;i++) scanf("%d",tar+i); 59 for(int i=0;i<full&&!sol;i++) { 60 for(int j=0;j<1<<n;j++) in[0][j] = j; 61 unzipsta(i,n_nod,m_nod) , solve(0,0,n_nod-1,0,n_lev-1,1<<n); 62 if( !dif(n,n_lev) ) sol = 1; 63 } 64 if( !sol ) puts("-1"); 65 else { 66 for(int i=0;i<n_nod;i++) { 67 for(int j=0;j<m_nod;j++) putchar(\'0\'+sta[i][j]); 68 putchar(\'\\n\'); 69 } 70 } 71 putchar(\'\\n\'); 72 } 73 return 0; 74 }
正解代码:
(原谅我代码写得像天书一样)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define debug cout using namespace std; const int maxe=30,maxn=1<<14; bool ans[maxe][maxn]; int in[maxe][maxn],Log[maxn]; int lim[maxn],app[maxn],rapp[maxn]; int n,nod_n,nod_m,data_m; inline int shl(int x,int l) { // looping bit left move . int ret = 0; for(int i=0;i<l;i++) if( x & ( 1 << i ) ) ret |= 1 << ( ( i - 1 + l ) % l ); return ret; } inline int shr(int x,int l) { // looping bit right move . int ret = 0; for(int i=0;i<l;i++) if( x & ( 1 << i ) ) ret |= 1 << ( ( i + 1 ) % l ); return ret; } inline void dfs(int inlev,int outlev,int cur,int pos) { // cur = 0 or 1 means up or down . int oth = pos ^ 1; if( !cur ) { // solve by inklev . if( !~lim[in[inlev][oth]] ) lim[in[inlev][oth]] = lim[in[inlev][pos]] ^ 1 , dfs(inlev,outlev,0,oth); int op = app[in[inlev][pos]] , oop = op ^ 1; if( !~lim[in[outlev][oop]] ) lim[in[outlev][oop]] = lim[in[outlev][op]] ^ 1 , dfs(inlev,outlev,1,oop); } else { if( !~lim[in[outlev][oth]] ) lim[in[outlev][oth]] = lim[in[outlev][pos]] ^ 1 , dfs(inlev,outlev,1,oth); int op = rapp[in[outlev][pos]] , oop = op ^ 1; if( !~lim[in[inlev][oop]] ) lim[in[inlev][oop]] = lim[in[inlev][op]] ^ 1 , dfs(inlev,outlev,0,oop); } } inline void solve(int sx,int sy,int tx,int inlev,int outlev,int n) { // n is count of points , n >> 1 is count of nodes , sx and sy are position of nodes . if( n == 2 ) { if( in[inlev][sy<<1] != in[outlev][sy<<1] ) ans[sx][sy] = 1; return; } for(int i=0;i<n;i++) lim[in[inlev][(sy<<1)+i]] = -1; for(int i=0;i<n;i++) app[in[outlev][(sy<<1)+i]] = (sy<<1)+i , rapp[in[inlev][(sy<<1)+i]] = (sy<<1)+i; const int mid = ( sy << 1 ) + ( n >> 1 ) - 1; for(int i=0;i<n>>1;i++) { int l = (sy<<1) + (i<<1) , r = (sy<<1) + (i<<1|1) , tl = (sy<<1) + shl(i<<1,Log[n]) , tr = (sy<<1) + shl(i<<1|1,Log[n]); if( !~lim[in[inlev][l]] && !~lim[in[inlev][r]] ) lim[in[inlev][l]] = tl > mid , lim[in[inlev][r]] = tr > mid; // left 0 , right 1 . else { // solve limit . if( ~lim[in[inlev][l]] ) lim[in[inlev][r]] = lim[in[inlev][l]] ^ 1; else lim[in[inlev][l]] = lim[in[inlev][r]] ^ 1; ans[sx][sy+i] = ( tl > mid ) ^ lim[in[inlev][l]]; } dfs(inlev,outlev,0,l) , dfs(inlev,outlev,0,r); int pl = app[in[inlev][l]] , bpl = ( pl - (sy<<1) ) >> 1 , bpl_l = (sy<<1) + (bpl<<1) , bpl_l_tarsou = (sy<<1) + shl(bpl<<1,Log[n]); ans[tx][sy+bpl] = ( bpl_l_tarsou > mid ) ^ lim[in[outlev][bpl_l]]; int pr = app[in[inlev][r]] , bpr = ( pr - (sy<<1) ) >> 1 , bpr_l = (sy<<1) + (bpr<<1) , bpr_l_tarsou = (sy<<1) + shl(bpr<<1,Log[n]); ans[tx][sy+bpr] = ( bpr_l_tarsou > mid ) ^ lim[in[outlev][bpr_l]]; } for(int i=0;i<n>>1;i++) { // trans data to next level . int l = (sy<<1) + (i<<1) , r = (sy<<1) + (i<<1|1) , tl = (sy<<1) + shl(i<<1,Log[n]) , tr = (sy<<1) + shl(i<<1|1,Log[n]); in[inlev+1][tl] = in[inlev][l] , in[inlev+1][tr] = in[inlev][r]; if(ans[sx][sy+i]) swap(in[inlev+1][tl],in[inlev+1][tr]); } for(int i=0;i<n>>1;i++) { // trans data to previous level . int l = (sy<<1) + (i<<1) , r = (sy<<1) + (i<<1|1) , fl = (sy<<1) + shl(i<<1,Log[n]) , fr = (sy<<1) + shl(i<<1|1,Log[n]); in[outlev-1][fl] = in[outlev][l] , in[outlev-1][fr] = in[outlev][r]; if(ans[tx][sy+i]) swap(in[outlev-1][fl],in[outlev-1][fr]); } solve(sx+1,sy,tx-1,inlev+1,outlev-1,n>>1) , solve(sx+1,sy+(n>>2),tx-1,inlev+1,outlev-1,n>>1); } inline void reset() { memset(ans,0,sizeof(ans)); } int main() { while( scanf("%d",&n) == 1 && n ) { reset() , nod_n = ( n << 1 ) - 1 , nod_m = 1 << ( n - 1 ) , data_m = 1 << n; for(int i=0;i<=n;i++) Log[1<<i] = i; for(int i=0;i<data_m;i++) in[0][i] = i , scanf("%d",in[nod_n]+i); solve(0,0,nod_n-1,0,nod_n,data_m); for(int i=0;i<nod_n;i++) { for(int j=0;j<nod_m;j++) putchar(\'0\'+ans[i][j]); putchar(\'\\n\'); } putchar(\'\\n\'); } return 0; }
T2:
观察这个生成的序列,发现没什么性质(没错,它最大的性质就是随机!)。
于是没什么好办法,只好用数据结构做了。
平衡树和线段树是不用想了,铁定TLE(线段树还会MLE),我们需要一个更优美的做法。
用堆!一个大根堆维护序列的前半部分,一个小根堆维护序列的后半部分。
每次把新生成的数插入前半部分的大根堆,当这个堆的大小比(i+1)/2大时,弹出堆顶并把堆顶放进后半部分的小根堆。
如果当前大根堆堆顶比小根堆堆顶大的话,我们就交换这两个堆顶的元素(两个push两个pop)。
显然对于插入的每个数字,第一种操作只会进行1次,对于每一个i,第二种操作只会进行O(1)次,总复杂度O(nlogn)。
很不幸的是,这样做也只有50分,即使你用pb_ds的配对堆或斐波那契堆。
考虑复杂度瓶颈在哪里,我们每次对堆进行操作的时候,都要承受一个巨大的log。
好的,我们还是维护两个堆,在中间维护一棵平衡树作为缓存,保证当前查询的那个数值永远在平衡树里。用这棵平衡树减少堆的操作并限制它的大小来减小log,是不是就能卡过去了呢?
具体操作就是,插入的时候判断新加的值在那一段,加入正确的位置;
当平衡树的大小和第一个堆大小的和比当前要查询的数值小时,从第二个堆中取出数字放进平衡树中;
当平衡树的大小比阈值大时,把平衡树开始或结尾的元素移动到大小较小的那个堆中。
因为数据随机,所以如果平衡树缓存大小合适,我们对两边的堆的操作会大幅度减少,于是就可以AC了。
50分暴力代码:
1 #pragma GCC optimize("Ofast") 2 #pragma GCC target("avx") 3 #pragma GCC optimize("no-stack-protector") 4 #include<cstdio> 5 #include<ext/pb_ds/priority_queue.hpp> 6 #define ull unsigned long long 7 using namespace std; 8 using namespace __gnu_pbds; 9 const unsigned mod=1e9+7; 10 11 __gnu_pbds::priority_queue<unsigned,less<unsigned>,binary_heap_tag> exs; 12 __gnu_pbds::priority_queue<unsigned,greater<unsigned>,binary_heap_tag> del; 13 14 __inline unsigned gen(const unsigned &t,const unsigned &ans) { 15 return ( 714636908ull * t % mod + 681692770u ) * ( 846930886ull * ans % mod + 804289376u ) % mod; 16 } 17 18 int main() { 19 static unsigned n,t,ans,out; 20 scanf("%u%u",&n,&t); 21 for(register unsigned i=1,p=1;i<=n;i++,p=(i+1)>>1) { 22 exs.push(t); 23 while( exs.size() > p ) del.push(exs.top()) , exs.pop(); 24 while( del.size() && del.top() < exs.top() ) exs.push(del.top()) , del.pop() , del.push(exs.top()) , exs.pop(); 25 ans = exs.top() , out ^= ans , t = gen(t,ans); 26 } 27 printf("%u\\n",out); 28 return 0; 29 }
正解代码:
1 /*#pragma GCC optimize("Ofast") 2 #pragma GCC target("avx") 3 #pragma GCC optimize("no-stack-protector")*/ 4 #include<cstdio> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #include<ext/pb_ds/tree_policy.hpp> 9 #include<ext/pb_ds/assoc_container.hpp> 10 typedef unsigned long long int ulli; 11 const int mod=1e9+7; 12 const int lim=10; 13 14 std::priority_queue<ulli,std::vector<ulli>,std::less<ulli> > lft; 15 std::priority_queue<ulli,std::vector<ulli>,std::greater<ulli> > rit; 16 __gnu_pbds::tree<ulli,__gnu_pbds::null_type,std::less<ulli>,__gnu_pbds::rb_tree_tag,__gnu_pbds::tree_order_statistics_node_update> buf; 17 18 __inline unsigned gen(const unsigned &t,const unsigned &ans) { 19 return ( 714636908ull * t % mod + 681692770u ) * ( 846930886ull * ans % mod + 804289376u ) % mod; 20 } 21 22 int main() { 23 static unsigned n,t,ans,out; 24 scanf("%u%u",&n,&t); 25 for(register unsigned i=1,p;i<=n;i++) { 26 p = ( i + 1 ) >> 1; 27 register ulli cur = (ulli) t * ( mod + 1 ) + i; 28 if( buf.size() && cur < *buf.begin() ) lft.push(cur); 29 else if( buf.size() && cur > *buf.rbegin() ) rit.push(cur); 30 else buf.insert(cur); 31 while( p > lft.size() + buf.size() ) buf.insert(rit.top()) , rit.pop(); 32 while( p <= lft.size() ) buf.insert(lft.top()) , lft.pop(); 33 out ^= ans = *buf.find_by_order(p-lft.size()-1) / ( mod + 1 ) ; 34 t = gen(t,ans); 35 while( buf.size() > lim ) { 36 if( lft.size() < rit.size() ) lft.push(*buf.begin()) , buf.erase(buf.begin()); 37 else rit.push(*buf.rbegin()) , buf.erase(buf.rbegin()); 38 } 39 } 40 printf("%u\\n",out); 41 return 0; 42 }
T3:
博弈论+数据结构?
SG函数怎么推啊......只知道P为奇数时,SG值为当前元素的奇偶性。数据不保证P的奇偶,写了也不一定有分,弃疗了。
假设我们打表观察(出题人写的)这个题的SG函数是这样的:
当P为奇数时,大小为n的堆的SG值为 n mod 2。
当P为偶数时,大小为n的堆的SG值为:
如果 n mod P + 1 = P , 则为2;
否则为 n mod P + 1 mod 2。
考虑怎么证明。
P为奇数时的SG值显然,因为每次操作一定改变堆大小的奇偶性。
P为偶数时的证明就比较麻烦了......
因为a^2-1=(a+1)*(a-1),故 a^2-1+1=(a+1)*(a-1)+1 = 1 mod a+1 , 所以 a^3 = a * a^2 = a mod a+1。
所以,取p^k的情况,在mod p+1下都能划归为取1或者取p的情况。
所以我们可以在P+1的同余系下做。如果 mod P+1 < P 的话,只能取1,所以SG值为 n mod P + 1 mod 2 。
而mod P+1 = P的情况,我们可以取p,到SG值为0的状态0和SG值为1的状态P-1,故这个状态的SG值为mex(1,2)=2。
于是我们对于P为奇数和偶数的情况分别计算。
P为奇数的话,相当于区间 xor 1,求区间 xor 和,线段树轻松搞定。
P为偶数的情况,我们需要分块。
考虑我们先让所有数值mod P+1再按照mod 2将这些数值放入两个有序数组里。
那么,如果区间同时加上add,所有>=P+1-add的数会溢出一轮。
如果add为偶数的话,溢出的数的奇偶性改变,没溢出的数的奇偶性不变;
如果add为奇数的话,溢出的数的奇偶性不变,没溢出的数的奇偶性改变。
这样我们维护两个有序数组每次lower_bound一下就好了,当然如果你非得写平衡树也没人拦你。
(什么?mod P+1 = P的?满足这种情况的只有一个数就是P+1-add,我们用一个map存mod P+1为某个数的值有多少个,再手动从奇偶两种减去这个值的贡献就好了)
代码:
1 #pragma GCC optimize("Ofast") 2 #include<cstdio> 3 #include<algorithm> 4 #include<map> 5 #include<vector> 6 #include<cmath> 7 const int maxn=1e5+1e2,maxe=325; 8 9 int n,m,a; 10 11 namespace Even { 12 int dat[maxn],bel[maxn],st[maxe],ed[maxe],add[maxe]; 13 std::vector<int> cont[maxe][2]; 14 std::map<int,以上是关于20180516小测的主要内容,如果未能解决你的问题,请参考以下文章