[考试反思]0224省选模拟30:仓皇

毒瘤大合集。

$T1$基本没有暴力分但是正解是两个暴力的合集。。然后单纯只写其中比较简单的那一个的话一分都没有。。

$T2$写的暴力正解不知道是啥东西。但是我数组开i太大,本机没事但交上去$CE$了。(把我根本没调用过的杜教筛哈希表给删了就不$CE$了)

然而也只有$10$分。常数丑了。把所有数组都缩小到$\frac{1}{10}$就有$20$分了。奇奇怪怪。

$T3$是个毒瘤卡常的交互题,复杂度明明是$O(n \ log \ n)$但是因为所谓实测非得卡到$O(10n)$.

然而我并没有什么想法,毕竟只会写$O(n^2)$的,正解优化的思路还是不错。但是因为毒瘤卡常所以还是耽误了我一下午的时间。

不知道为什么并没有留更多的改题时间,所以苟过$T3$然后把$T1$的高分暴力写了就结束了。

啊啊啊我的视频课啊。。。

 

T1:任凭风浪起,稳坐钓鱼台

大意:给$n$个$k$位二进制数,求两两异或的立方和。$nk \le 2 \times 10^6$

对于$k \geq 128$直接暴力,手写$bitset$压位,$2^{64}$进制效果不错。

对于$k<128$,逐位考虑贡献。发现立方就是可重复的选择三位,如果这三位在两个数中异或值都为$1$那么就产生这三位的乘积的贡献。

于是用$bitset$压一压有哪些数这一位上是1哪些数是0,然后与一与就好了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ull unsigned long long
 4 #define mod 998244353
 5 unsigned int seed;int n,k,x,ans,bit[1<<16];ull jz=((1ull<<63)%mod<<1)%mod,pw[555];
 6 vector<ull>b[20005],r,c[20005];
 7 ull get01(){
 8    seed^=seed<<13;
 9    seed^=seed>>17;
10    seed^=seed<<5;
11    return seed&1;
12 }
13 int cnt(ull s){return bit[s&65535]+bit[s>>16&65535]+bit[s>>32&65535]+bit[s>>48];}
14 int main(){
15     cin>>n>>k>>x>>seed;
16     if(k>=128){
17         for(int i=1;i<=n;++i)b[i].resize((k>>6)+1);r.resize((k>>6)+1);
18         for(int i=1;i<=n;++i)for(int j=0;j<k;++j)b[i][j>>6]|=get01()<<(j&63);
19         const int K=k-1>>6;
20         for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j){
21             for(int x=0;x<=K;++x)r[x]=(b[i][x]^b[j][x])%mod;
22             ull tot=0,p=1;
23             for(int x=0;x<=K;++x)tot=(tot+p*r[x])%mod,p=p*jz%mod;
24             ans=(ans+tot*tot%mod*tot)%mod;
25         }cout<<ans<<endl;return 0;
26     }const int N=n-1>>6; r.resize(N+1);
27     for(int i=0;i<k;++i)b[i].resize(N+1),c[i].resize(N+1);
28     for(int i=0;i<n;++i)for(int j=0;j<k;++j)b[j][i>>6]|=get01()<<(i&63),c[j][i>>6]|=b[j][i>>6]&(1ull<<(i&63))^(1ull<<(i&63));
29     pw[0]=1;
30     for(int i=1;i<555;++i)pw[i]=(pw[i-1]+pw[i-1])%mod;
31     for(int i=1;i<1<<16;++i)bit[i]=bit[i>>1]+(i&1);
32     for(int i=0;i<k;++i)for(int j=i;j<k;++j)for(int x=j;x<k;++x){
33         int _000=0,_001=0,_010=0,_011=0,_100=0,_101=0,_110=0,_111=0;
34         for(int y=0;y<=N;++y)
35             _000+=cnt(c[i][y]&c[j][y]&c[x][y]),
36             _001+=cnt(c[i][y]&c[j][y]&b[x][y]),
37             _010+=cnt(c[i][y]&b[j][y]&c[x][y]),
38             _011+=cnt(c[i][y]&b[j][y]&b[x][y]),
39             _100+=cnt(b[i][y]&c[j][y]&c[x][y]),
40             _101+=cnt(b[i][y]&c[j][y]&b[x][y]),
41             _110+=cnt(b[i][y]&b[j][y]&c[x][y]),
42             _111+=cnt(b[i][y]&b[j][y]&b[x][y]);
43         int r=i==x?1:(i==j||j==x?3:6);
44         ans=(ans+(1ll*_000*_111+1ll*_001*_110+1ll*_010*_101+1ll*_011*_100)%mod*pw[i+j+x]*r)%mod;
45     }cout<<ans<<endl;
46 }
80pts。卡常

对于异或这种操作,上面我们是枚举了每一位的$01$。然而为什么不能直接去做异或呢?

发现这道题貌似可以直接对若干位异或$fwt$。

我们把二进制位每$a$位分为一组,然后我们要从中选出三个数位的话,那么只需要选出三个数位分别在哪一组里。

 

 

T2:任凭风浪起,稳坐钓鱼台(续)

鸽了鸽了等$skyh$大神讲了。

 

T3:鱼和熊掌不可兼得

大意:交互。实现一个函数猜中一个$n$排列。可以调用一个函数,返回排列中对位正确的有多少个。$n \le 5000,limit \le 10n$

首先为了方便随机出一个错排。

然后交换关系相当与$\frac{n(n-1)}{2}$条边。

因为已经是错排了,所以交换的结果只会使正确数增加,那么这条边就使至少一个数字归位了。

把所有这样有用的边都记下来,那么整个序列就被分成了若干环。

现在问题是如何在合法查询次数内知道哪条边是否有用。

发现只要没有公共点,那么边之间是互不影响的。

那么我们就可以指定一个没有公共点的边集同时交换它们然后进行$count$。如果有数就证明这组边里至少有一条有用的。

然后递归分治下去就能找到哪些边是有用的。

所以现在的问题是如何把$\frac{n(n-1)}){2}$条边分成尽量少的组使之没有公共点。

发现边两端点的编号和对$n$取模就是一个合理的组编号。

因为如果有公共点那么相当于一个数就一定锁定了。为了让模后相等在同一个组内,那么另一个数也要相等,那就是重边了。。。

所以如此分组然后分治,最后$dfs$弄环就可以了。

然而有几个小细节用来卡常,毕竟这题应该是$O(nlogn)$的且附带一些不小的常数,然而卡成$10n$也就非常无良了。

首先对于一个点如果它的有用边度数已经达到了2那么以后关于这个点的边就不必加入分治了。

其次分治时传一个参数是这里面所有边对$count$的总贡献。然后查询左儿子时,右儿子的值也就可以直接做差得到了。

再之最后$dfs$的复杂度应该是环数级别的,而不能是点数级别的。

然后就大概在最后一个子任务$49199$过去了。无良出题人。。

 1 #include"game.h"
 2 using namespace std;
 3 #define S 5555
 4 int x[5555],y[5555],c,mt[5555],e[5555][3],al[5555],C,top;vector<int>a,r;
 5 void cl(int l,int r){for(int i=l;i<=r;++i)swap(a[x[i]],a[y[i]]);}
 6 void DaC(int l,int r,int zz){
 7     if(l==r){
 8         e[x[l]][++mt[x[l]]]=y[l];e[y[l]][++mt[y[l]]]=x[l];
 9         if(zz^1)e[x[l]][++mt[x[l]]]=y[l],e[y[l]][++mt[y[l]]]=x[l]; return;
10     }
11     cl(l,l+r>>1);int $=count(a);cl(l,l+r>>1);if($)DaC(l,l+r>>1,$);
12     if($!=zz)DaC((l+r>>1)+1,r,zz-$);
13 }
14 void dfs(int p,int nt=1){
15     al[p]=nt;top++;
16     for(int i=1;i<3;++i)if(al[e[p][i]]!=nt)swap(a[p],a[e[p][i]]),dfs(e[p][i],nt);
17 }
18 vector<int>guess(int n,int l){
19     a.resize(n);r.resize(n);
20     for(int i=1;i<=n;++i)a[i-1]=i;
21     srand(time(0));
22     while(count(a))random_shuffle(a.begin(),a.end());
23     for(int i=0;c=0,i<n;++i){
24         for(int j=0;j<n;++j)if(mt[j]!=2&&mt[(i-j+n)%n]!=2&&j<(i-j+n)%n)x[++c]=j,y[c]=(i-j+n)%n;
25         if(c){cl(1,c);int x=count(a);cl(1,c);if(x)DaC(1,c,x);}
26     }
27     for(int i=0,lc=0;i<n;++i)if(!al[i]){
28         top=1;
29         for(int j=0;j<n;++j)r[j]=a[j];
30         al[i]=1;swap(a[i],a[e[i][1]]);dfs(e[i][1]);
31         int C=count(a);if(C==lc+top){lc=C;continue;}
32         for(int j=0;j<n;++j)a[j]=r[j];lc+=top;
33         al[i]=2;swap(a[i],a[e[i][2]]);dfs(e[i][2],2);
34     }return a;
35 }
View Code

 

posted @ 2020-02-26 07:26  DeepinC  阅读(...)  评论(...编辑  收藏