[考試反思]1026csp-s模擬測試89:不公

稍垃圾。由於T1沒A。ios

趕巧前一段時間學了杜教篩,結果由於教練放錯題。數組

而後考場上瘋狂yy,最後水到了一個AC。ide

其實的確挺不公平的,很多人也沒學呢。函數

若是隻算T1和T3的分數的話,那70分就是個垃圾。spa

還有。。。。code

又一次蓋掉本身70分。。。blog

想剪枝結果打錯了。遞歸

在沒有用堆跑Dijk時由於隊列裏的元素無序因此一個點可能被擴展屢次,而有時候後擴展的狀態更優,因此不能打標記continue。隊列

其他其實還好,發揮的湊和吧。其實能yy出T2仍是挺不容易的。string

可是固然在正經考試裏不會出現你學過別人沒學過的知識點,因此沒必要竊喜。。。

 

T1:666

最優決策的形式必定是複製並連粘幾遍,再刪除幾回,循環。

也就是用n的費用×n,用1的費用-1。

跑最短路,由於邊權不大因此不必開堆。

打表發現,乘的形式只有2357是有效的,因此只有5種轉移。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 int dp[1000001],q[10000005];
 6 #define maxn 1000000
 7 int main(){
 8     memset(dp,30,sizeof dp);
 9     dp[1]=0;q[1]=1;
10     for(int h=1,t=1;h<=t;++h){
11         int a=q[h];
12         if(a*2<=maxn&&dp[a*2]>dp[a]+2)dp[a*2]=dp[a]+2,q[++t]=a*2;
13         if(a*3<=maxn&&dp[a*3]>dp[a]+3)dp[a*3]=dp[a]+3,q[++t]=a*3;
14         if(a*5<=maxn&&dp[a*5]>dp[a]+5)dp[a*5]=dp[a]+5,q[++t]=a*5;
15         if(a*7<=maxn&&dp[a*7]>dp[a]+7)dp[a*7]=dp[a]+7,q[++t]=a*7;
16         if(a&&dp[a-1]>dp[a]+1)dp[a-1]=dp[a]+1,q[++t]=a-1;
17     }
18     int n;scanf("%d",&n);printf("%d\n",dp[n]);
19 }
View Code

 

T2:123567

講過無數遍的式子

$\sum\limits_{i=1}^{\sqrt{n}} \mu(i) \frac{n}{i^2}$

而後後面那個東西,一個是莫比烏斯函數能夠用杜教篩弄前綴和,另外一個看着像除法分塊

只要設$d=i^2$就和普通的整除分塊沒有區別了

杜教篩篩莫比烏斯函數的式子是:

設$S_n=\sum\limits_{i=1}^{n} \mu(i)$

則$S_n= 1-\sum\limits_{i=2}^{n} S_{\frac{n}{i}}$

套一個整除分塊就能夠遞歸求解了。

線篩預處理3e7之內的$S_n$,而後剩下的繼續杜教。

複雜度並不會證,總之能夠接受。

作出了《奇怪的道路》的感受。。。好久好久好久沒有這種全場只有我本身A的題了,雖然說不公平可是仍是較開心,畢竟我愈來愈菜了

 1 #include<cstdio>
 2 #include<unordered_map>
 3 #include<cmath>
 4 using namespace std;
 5 #define C 33333333
 6 unordered_map<int,int>M;
 7 int p[20000005],pc,miu[C+5];bool np[C+5];
 8 int sum_miu(int n){
 9     if(n<=C)return miu[n];
10     if(M.find(n)!=M.end())return M[n];
11     int ans=1;
12     for(int l=2,r;l<=n;l=r+1)r=n/(n/l),ans-=sum_miu(n/l)*(r-l+1ll);
13     return M[n]=ans;
14 }
15 int main(){miu[1]=1;//freopen("data","r",stdin);
16     for(int i=2;i<=C;++i){
17         if(!np[i])p[++pc]=i,miu[i]=-1;
18         for(int j=1;j<=pc&&i*p[j]<=C;++j)
19             if(i%p[j])np[i*p[j]]=1,miu[i*p[j]]=-miu[i];
20             else{np[i*p[j]]=1;break;}
21     }
22     for(int i=1;i<=C;++i)miu[i]+=miu[i-1];
23     long long n,ans=0;scanf("%lld",&n);
24     for(long long l=1,r;l*l<=n;l=r+1)r=sqrt(n/(n/l/l)),ans+=(sum_miu(r)-sum_miu(l-1))*(n/l/l);
25     printf("%lld\n",ans);
26 }
View Code

 

T3:椎

%%%mikufun %%%Dybala

mikufun:板子題

Dybala:這不很簡單嗎?

的確是板子,線段樹維護區間單調棧以前就不是很理解。

可是就算作過了一邊依然不感受「很簡單」啊。。。反正%%%就是了

有空最好再寫一遍

其實差很少是照着板子抄下來的。。。可是此次確實理解了,就差再寫一遍了

對於這道題,咱們把它按照key拍到序列上。

而後每一個點在樹上的深度,就是從1到i不斷加入w維護單調遞減棧,再從max到i不斷加入w維護另外一個單調遞減棧,兩個單調棧的最終size和就是深度。

而求lca就是求序列上兩個點之間w最大的那個點。

由於知足堆性質,因此w大的是祖先,w最大的就是lca啦(由於也知足二叉搜索樹性質,因此在序列兩點之間就表明兩個點分別在左右子樹裏)

至於爲何是單調棧的size和,其實就是若是它在樹裏往上走有一條向左上的邊那麼右側單調棧就會多一個元素,左側同理。

具體含義的話畫個圖理解一下,對着樣例%一%就出來了。

因此如今的問題就大概是,從1到i的單調棧有幾個元素,支持插入,刪除。

其實沒必要單獨寫刪除操做,直接把那個點的w值設爲0就完事了。

首先咱們要查詢最大值,由於要求lca。

可是線段樹太大的話很麻煩。。。因此須要離線離散化,在離散化以後就能夠用數組實現key和w之間的映射了,而不用map什麼的。。。

這樣的話咱們原本要查詢w最大值對應的key值,在線段樹上須要返回pair還要維護一大堆東西。。。

離散化以後,由於題目保證key和w同一時刻均不重複,因此就能夠經過最大值直接獲得位置了。

%%%mikufun

可是重頭戲顯然不是區間查詢最大值的啦。。。

怎麼用線段樹維護單調棧?

正向單調棧和逆向單調棧是徹底同樣的,下面只以正向爲例。

函數askl(p,l,r,x)表示當前節點是p,(l,r)區間內的元素造成的單調棧在加入x元素進行彈棧以後單調棧裏剩餘的元素個數。

咱們須要的就是ask(1,1,i,0),由於加入0什麼影響都沒有(0是最小的固然不會彈棧)

這個如何遞歸求解?

若是是葉節點,固然好說,只要葉節點的元素大於x就能夠被留下。

不然的話,若是查詢的區間徹底包含了當前節點的區間:

若是右兒子的最大值小於x,那麼在彈棧的時候右兒子的全部點都會被彈掉,沒有貢獻,答案就是左兒子的貢獻,ask(p<<1,l,r,x),遞歸求解不用管

若是右兒子的最大值大於x,那麼右兒子的最大值會被加入棧,它被加入的時候可能會彈掉左兒子的一部分,就是ask(p<<1,l,r,mx[p<<1|1])+ask(p<<1|1,l,r,x)--1

(不考慮等於的問題,由於本題保證不相同)

而咱們能夠發現,查詢的第一項ask(p<<1,l,r,mx[p<<1|1])與詢問的參數x徹底無關,因此能夠預處理出來存在數組裏面。

再否則就只剩下一種狀況了,那就是詢問區間並無包含當前節點的整個區間,那麼就要像普通線段樹同樣遞歸求解了。

可是有一個地方相似於剪枝。就是說咱們每次在查詢完整的右兒子區間時(就是上面紅色的--1標記那裏),都保證了它的最大值mx[p<<1|1]被加入了棧裏。

那麼它就會把全部靠左的區間的小於mx[p<<1|1]的元素都彈掉。

這樣的話咱們在遞歸求解時就須要先查詢右兒子來保證它把左邊的該彈的都彈掉了。

因此咱們維護一個全局變量mxr,每次查詢完整右兒子及葉節點時(即--1處再加上葉節點的特判)都用區間最大值更新一下mxr。

而後你在詢問完全部右兒子以後纔會問到某一個左兒子,即保證了查詢每個區間以前,它右邊的全部節點都已經考慮到了。

這樣的話就能保證彈棧是正確的了。

那麼詢問區間不包含當前節點區間的狀況下,答案就是(qr>mid?askl(rc,ql,qr,P,mid+1,cr):0)+(ql<=mid?askl(lc,ql,qr,mxr,cl,mid):0);

(粘的代碼內語句,ql,qr表示詢問區間,cl,cr表示節點控制區間,lc,rc是左右兒子,P就是上文中的x)

如今的問題就是所說的預處理了,其實不是預處理,而是在每次修改時線段樹的update函數怎麼寫?

也就是在上面的定義同樣,若是你預處理的那個數組叫作upl的話,那麼只要這麼更新:

 upl[p]=askl(lc,cl,mid,mx[rc],cl,mid); 

完事了。

而後維護 右邊單調棧的話有些左右兒子關係須要翻轉,再也不贅述。

一個很棒的作法是,開兩個線段樹,把序列翻轉,直接用相同的函數再跑一遍,這樣寫的會簡單一些。(但我沒這麼寫)%%LNC

而後就是碼了。

細節較多,稍注意一下。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 #define S 6666666
 5 #define mid (cl+cr>>1)
 6 #define lc p<<1
 7 #define rc p<<1|1
 8 int mx[S],upl[S],upr[S],mxr,r[S],k[S],v[S],o[S],tp,MX,rw[S],rf[S];
 9 int askl(int p,int ql,int qr,int P=0,int cl=1,int cr=MX){
10     if(p==1)P=mxr=rw[qr+1];
11     if(cl==cr)return mxr=max(mxr,mx[p]),mx[p]>P;
12     if(ql<=cl&&cr<=qr)
13         if(mx[rc]<P)return askl(lc,ql,qr,P,cl,mid);
14         else {
15             int x=askl(rc,ql,qr,P,mid+1,cr)+upl[p];
16             mxr=max(mxr,mx[p]);return x;
17         }
18     return (qr>mid?askl(rc,ql,qr,P,mid+1,cr):0)+(ql<=mid?askl(lc,ql,qr,mxr,cl,mid):0);
19 }
20 int askr(int p,int ql,int qr,int P=0,int cl=1,int cr=MX){
21     if(p==1)P=mxr=rw[ql-1];
22     if(cl==cr)return mxr=max(mxr,mx[p]),mx[p]>P;
23     if(ql<=cl&&cr<=qr)
24         if(mx[lc]<P)return askr(rc,ql,qr,P,mid+1,cr);
25         else {
26             int x=askr(lc,ql,qr,P,cl,mid)+upr[p];
27             mxr=max(mxr,mx[p]);return x;
28         }
29     return (ql<=mid?askr(lc,ql,qr,P,cl,mid):0)+(qr>mid?askr(rc,ql,qr,mxr,mid+1,cr):0);
30 }
31 int askmx(int p,int ql,int qr,int cl=1,int cr=MX){
32     if(ql<=cl&&cr<=qr)return mx[p];
33     return max(ql<=mid?askmx(lc,ql,qr,cl,mid):0,qr>mid?askmx(rc,ql,qr,mid+1,cr):0);
34 }
35 void insert(int p,int pos,int w,int cl=1,int cr=MX){
36     if(cl==cr)return mx[p]=w,(void)0;
37     if(pos<=mid)insert(lc,pos,w,cl,mid);
38     else insert(rc,pos,w,mid+1,cr);
39     mx[p]=max(mx[lc],mx[rc]);
40     upl[p]=askl(lc,cl,mid,mx[rc],cl,mid);
41     upr[p]=askr(rc,mid+1,cr,mx[lc],mid+1,cr);
42 }
43 int query(int l,int r){
44     if(l>r)l^=r^=l^=r;
45     int lca=rf[askmx(1,l,r)];
46     return askl(1,1,l-1)+askr(1,l+1,MX)+askl(1,1,r-1)+askr(1,r+1,MX)-2*(askl(1,1,lca-1)+askr(1,lca+1,MX));
47 }
48 int main(){
49     int n;scanf("%d",&n);
50     for(int i=1;i<=n;++i){
51         scanf("%d",&o[i]);
52         if(o[i]==0)scanf("%d%d",&k[i],&v[i]),r[++tp]=k[i],r[++tp]=v[i];
53         if(o[i]==1)scanf("%d",&k[i]);
54         if(o[i]==2)scanf("%d%d",&k[i],&v[i]);
55     }
56     sort(r+1,r+1+tp);MX=unique(r+1,r+1+tp)-r-1;
57     for(int i=1;i<=n;++i){
58         k[i]=lower_bound(r+1,r+1+MX,k[i])-r;
59         v[i]=lower_bound(r+1,r+1+MX,v[i])-r;
60         if(o[i]==0)rw[k[i]]=v[i],rf[v[i]]=k[i],insert(1,k[i],v[i]);
61         if(o[i]==1)insert(1,k[i],0);
62         if(o[i]==2)printf("%d\n",query(k[i],v[i]));
63     }
64 }
View Code
相關文章
相關標籤/搜索