[考試反思]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} \times 10^6 < MAX\ long\ long$

所以只要拿$10^{12}$做進制數寫個類似高精的東西,每次乘除都不會爆掉$long\ long$。還是挺好寫的。詳見代碼。

然後回到這道題上來,我們不難發現執行次數與$p_i$無關而只與$e_i$有關。這是指數上的東西,我們大膽猜測不會很多。

集合大小最大也就$18$。所有無序狀態數也不到$2 \times 10^5$。

不難發現所有集合都可以用$2^{e_1} \times 3^{e_2} \times 5^{e_3} \times 7^{e_4} \times ...(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 \times 10^5 \times 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\n",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\n",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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章