zoukankan      html  css  js  c++  java
  • [考试反思]0607四校联考第二轮day1:恍惚

    这博客咕了9天,再咕没机会写了XD

    然而考得依旧不怎么样。

    $T1$的暴力是个记搜类似物,非常开心的写,本机跑的出$70$的点,于是很开心的交了。

    然后因为是期望题实在不会手模,过了看似不小的样例然后就没再管正确性,然后爆掉了。

    $T2$是个大分类讨论题,但是由于自己思路并不清晰,所以还是$WA$掉了

    按照惯例$T3$是神仙题全场最高$15$,也就没有写。然后就炸了。

    T1:随机除法(div)

    大意:多测。给你一个$n$,并给出所有质因子$p_i$,每次将$n$除掉它所有因子中的随机一个。求期望多少次之后会变成$1$。$p_i le 10^6,n le 10^{24},T le 10^5$

    首先肯定会想把这个$n$分解为$p_i^{e_i}$的形式。但是$n$挺大的,要写高精?

    事实上,$10^{24}$这个数据范围给的还是比较合适的,$sqrt{10^{24}}=10^{12},10^{12} imes 10^6 < MAX long long$

    所以只要拿$10^{12}$做进制数写个类似高精的东西,每次乘除都不会爆掉$long long$。还是挺好写的。详见代码。

    然后回到这道题上来,我们不难发现执行次数与$p_i$无关而只与$e_i$有关。这是指数上的东西,我们大胆猜测不会很多。

    集合大小最大也就$18$。所有无序状态数也不到$2 imes 10^5$。

    不难发现所有集合都可以用$2^{e_1} imes 3^{e_2} imes 5^{e_3} imes 7^{e_4} imes ...(e_1 ge e_2 ge e_3 ge e_4 ge ...)$的形式表示出来。

    状态数也不多所以直接搜出来,每次走到的都一定是一个没有被走到过的集合。

    然后我们考虑怎么求出每种集合的$dp$值。除了自环之外,转移一定是$DAG$。每种质因子都有可能减少或不变。

    这好像就是高维前缀和的形式。然后一个比较经典但是总被遗忘的做前缀和的方法就是:依次对每一维做前缀和。

    具体而言就是,先让第一维做前缀和剩下维不变,然后用得到的数组给第二维做前缀和,此时得到的就是前两维都做过前缀和的数组。一直做下去就好。

    还有一个问题是怎么快速的用一个集合得到一个数组下标。不难想到哈希。但是这道题需要支持 某一维$-1$之后还要保证元素无序(小的必须在前面)

    如果每次都是$-1$然后$sort$就会$TLE$。所以我们需要一个更优的哈希函数满足:无序,可单位减。

    这里$skyh$大神提供了一个巧妙的方法:任取一个常数$c$,然后构造$hash=sum c^{e_i}$。显然满足了以上性质。

    于是本题得到了解决。时间复杂度$O(2 imes 10^5 imes 18 + T log n)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define ull unsigned ll
     5 const int p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71},S=3e6+7,mod=1e9+7,s=2e5;
     6 const ll Mod=1000000000000ll;
     7 int cnt,f[s][20],v[s][20],dc[s],dvs[20],dp[s]; ull pw[90],Hsh[s];
     8 struct hash_map{
     9     int fir[S],l[S],v[S],ec;ull to[S];
    10     int&operator[](ull x){int r=x%S;
    11         for(int i=fir[r];i;i=l[i])if(to[i]==x)return v[i];
    12         l[++ec]=fir[r];fir[r]=ec;to[ec]=x;return v[ec]=-1;
    13     }
    14 }M;
    15 int qp(int b,int t=mod-2,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
    16 void sch(long double n,int pc,int lt,int f,int P){
    17     for(int i=0;i<19;++i)Hsh[P]+=pw[v[P][i]];
    18     M[Hsh[P]]=P;
    19     for(int i=1;i<=lt&&n/p[pc]>1;++i){
    20         v[++cnt][pc]=i; dc[cnt]=dc[P]*(i+1);
    21         for(int j=0;j<pc;++j)v[cnt][j]=v[P][j];
    22         sch(n/=p[pc],pc+1,i,P,cnt);
    23     }
    24 }
    25 int mo(int x){return x>=mod?x-mod:x;}
    26 int main(){
    27     freopen("div.in","r",stdin);freopen("div.out","w",stdout);
    28     for(int i=pw[0]=1;i<90;++i)pw[i]=pw[i-1]*23; dc[1]=cnt=1;
    29     sch(1e24,0,100,0,1);
    30     for(int i=2;i<=cnt;++i){
    31         for(int j=18;~j;--j)if(v[i][j])f[i][j]=mo(f[i][j+1]+f[M[Hsh[i]-pw[v[i][j]]+pw[v[i][j]-1]]][j]);
    32         dp[i]=(f[i][0]+dc[i])*1ll*qp(dc[i]-1)%mod;
    33         for(int j=0;j<=18;++j)f[i][j]=mo(f[i][j]+dp[i]);
    34     }
    35     char N[28];int m;ll hn,ln;
    36     while(scanf("%s%d",N,&m)==2){
    37         ln=hn=0; for(int i=0;i<19;++i)dvs[i]=0;
    38         for(int i=0;N[i];++i)ln=ln*10+N[i]-48,hn=hn*10+ln/Mod,ln%=Mod;
    39         for(int i=0,p;i<m;++i){
    40             scanf("%d",&p);
    41             while(((hn%p)*Mod+ln)%p==0)ln+=hn%p*Mod,hn/=p,ln/=p,dvs[i]++;
    42         }
    43         sort(dvs,dvs+m,[](int a,int b){return a>b;});
    44         ull hsh=0;
    45         for(int i=0;i<19;++i)hsh+=pw[dvs[i]];
    46         printf("%d
    ",dp[M[hsh]]);
    47     }
    48 }
    View Code

    T2:炮塔(tower)

    大意:给定一个由炮塔,干扰器,空地 组成的序列,你最开始在位置$1$。如果你在位置$x$且$x-1,x+1$都不是干扰器你就挂了。

    你每次可以向一个方向走一步,或者捡起当前位置的干扰器,或者如果手里有干扰器的话可以放在当前位置。求整个过程中你最多持有多少干扰器。$|S| le 10^5$

    大型分类讨论。

    如果当前格子是干扰器,那就捡起来呗。(如果需要放,那再说)

    如果当前格子是空地,那就往前走呗。

    否则当前格子一定是炮台,继续分类讨论:

      如果下一个格子是干扰器,那就往前走呗。

      (你会把那个干扰器捡起来,以前如果有需要手持一个干扰器才能捡的,以后就需要两个了,因为你把干扰器捡起来了,跨过炮台还需要一个)

      如果下一个格子是空地,那就在前一个格子放下一个干扰器然后往前走呗。

      (如果没干扰器了就不走了,否则如果你以后手里有其它至少一个干扰器,就可以回来把这个和前面的捡起来)

      再否则,下一个格子一定是炮台,继续再分类讨论:

        如果下下个格子是干扰器,那么就可以在前一个格子放一个干扰器然后继续往前走,捡起对面的干扰器,对各个变量没有影响。

        否则你一定走不过去。结束。

    始终维护四个变量:当前持有的,最大持有的,如果手里有一个能拿到的,如果手里有两个能拿到的。然后就可以写了。

    思路还是应该清晰一些,到文化课上也会有用的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 char s[6666666];int n,v1,v2,c,mx;
     4 int main(){freopen("tower.in","r",stdin);freopen("tower.out","w",stdout);int t;cin>>t;while(t--){
     5     scanf("%s",s);n=strlen(s);s[n++]='#';s[n++]='#';s[n++]='#';
     6     for(int i=0;;++i){
     7         if(s[i]=='*')c++;
     8         else if(s[i]=='.');
     9         else if(s[i+1]=='*')v2+=v1,v1=0;
    10         else if(s[i+1]=='.'){if(c)c--,v1++;else break;}
    11         else if(s[i+2]=='*'){if(c)i++,c--;else break;}
    12         else break;
    13         if(c>=1)c+=v1,v1=0;
    14         if(c>=2)c+=v2,v2=0;
    15         mx=max(mx,c);
    16     }printf("%d
    ",mx);mx=c=v1=v2=0;
    17 }}
    View Code

    T3:最大子段和

    大意:有一个长度$2n-1$的序列,偶数位置是空白的你可以在里面填上$[-K,K]$的任意数,最大化(最大子段和-最大的由正数构成的子段和)。$n le 5000$

    保证序列里的所有数都在$[-K,K]$中。

    首先可以感性理解一个结论,要么填$-1$要么填$K$.

    然后依然可以感性理解的一个结论,最大子段和一定是$[1,2n-1],[2,2n-1],[1,2n-2],[2,2n-2]$。你可以不断填$K$以做到这一点。

    然后再次感性理解一个结论,(对于所有奇数位置)有决策单调性。

    然后这题就可以做了。$dp[i][j]$表示考虑了前$i$个位置,填了$j$个$K$,此时最大的由正数构成的子段和。凭借第二维可以得出真正的最大子段和。

    然后决策单调性就可以用指针爆扫决策点(转移形式是$max(dp[x][j-1],sum[x+1][i])$,维护两个值相等的位置)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,dp[10001][2],A[10001],a[10001],K,ans;
     4 int cal(int l,int r){l--;return ~l?a[r]-a[l]+(r>>1)*K-(l>>1)*K:a[r]+(r>>1)*K;}
     5 int main(){
     6     freopen("subsegment.in","r",stdin);freopen("subsegment.out","w",stdout);
     7     scanf("%d%d",&n,&K); n<<=1;n--;
     8     for(int i=1;i<=n;i+=2)scanf("%d",&A[i]);
     9     if(A[1]<0)A[1]=0; if(A[n]<0)A[n]=0; A[0]=-1;
    10     for(int i=0,tot=0;i<=n;++i)a[i]=(i?a[i-1]:0)+A[i],tot=A[i]<0?0:(tot+(i&1?A[i]:K)),dp[i][0]=max(i?dp[i-1][0]:0,tot);
    11     ans=max(0,cal(0,n)-dp[n][0]);
    12     for(int j=1;j<=n/3;++j){
    13         int nw=j&1;
    14         for(int i=1,pt=0,lst=-1;i<=n;++i)if(A[i]>=0){
    15             dp[i][j]=1e9;
    16             while(pt<i&&(pt?dp[pt-1][nw^1]:0)+cal(0,pt)<=cal(0,i))pt+=2;
    17             if(pt-2<i&&pt-2>lst)dp[i][nw]=min(dp[i][nw],max(pt>=3?dp[pt-3][nw^1]:0,cal(pt-1,i)));
    18             if(pt<i)dp[i][nw]=min(dp[i][nw],max(pt?dp[pt-1][nw^1]:0,cal(pt+1,i)));
    19             if(~lst)dp[i][nw]=min(dp[i][nw],max(dp[lst-1][nw],cal(lst+1,i)));
    20         }else dp[i][nw]=1e9,lst=i,pt=i+3;
    21         ans=max(ans,cal(0,n)-(K+1)*j-dp[n][nw]);
    22     }printf("%d",ans+(n!=1));
    23 }
    View Code
  • 相关阅读:
    kubernetes----资源控制器5
    kubernetes----二进制安装3
    kubernetes----自动化安装2
    kubernetes----基础1
    PC微信低版本限制登录怎么办?
    mysql排名次
    mysql中GROUP BY中报错
    scrapy框架操作
    python QQ与微信自动发消息
    python中调用js的编码问题
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/13139585.html
Copyright © 2011-2022 走看看