zoukankan      html  css  js  c++  java
  • [考试反思]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 imes 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
  • 相关阅读:
    激活win10系统的方法(亲测)
    java使用jxl,自动导出数据excle,quartz自动发送邮件
    java对接网银支付案例
    将数字转换成Excel表头格式的字母序号
    mysql date_add日期函数的使用
    itext A4纸张横向创建PDF
    Spring 定时器 定时访问数据库并发送邮件
    mvc路由报错
    eval()和$.parseJSON()注意事项
    跨域学习笔记
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12359893.html
Copyright © 2011-2022 走看看