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
  • 相关阅读:
    AX ERROR: Could not find my mock parent, most likely I am stale 不及格的程序员
    利用Segue在视图控制器间传值的问题 不及格的程序员
    Creating a Singleton Instance 不及格的程序员
    iPad 通知 UIKeyboardWillShowNotification 不会在keyBoard处在Undock状态下接到通知 不及格的程序员
    Why RootViewController's view is rotated Automatically by System when the app first loaded? 不及格的程序员
    如何弹出UIDatePicker最好 不及格的程序员
    jQuery开始做恶了 不及格的程序员
    what is the SEL,id and IMP,Class ,Method? 不及格的程序员
    Objectivec 字符串比较的陷井 不及格的程序员
    Unable to create any keyboard shortcuts after the iOS 6.1.3 update on iPad. 不及格的程序员
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/13139585.html
Copyright © 2011-2022 走看看