1. 程式人生 > 其它 >Noip模擬36 2021.8.11

Noip模擬36 2021.8.11

T1 Dove打撲克 T2 Cicada與排序 T3 Cicada拿衣服

剛題的習慣還是改不了,怎麼辦???

T1 Dove打撲克

考場上打的動態開點線段樹+並查集,考後發現自己像一個傻子,並查集就行。。

這幾天惡補資料結構瘋了

用樹狀陣列維護字尾和,$siz_i$表示編號為$i$的牌堆的卡牌個數,並使用桶記錄一下這種數量級的牌堆的個數

同時使用$set$維護一下還存在的牌堆的編號

在合併的時候按照題目原理對各種維護陣列加加減減操作就行,注意在一類數量級的桶沒有東西之後,要將$set$中的相應元素刪掉

查詢的時候遍歷$set$中的元素,查詢即可。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3
using namespace std; 4 inline int read(){ 5 int x=0,f=1; char ch=getchar(); 6 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 7 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} 8 return x*f; 9 } 10 const int NN=1e5+5; 11 int
n,m,fa[NN],tr[NN],r,siz[NN],t[NN]; 12 set<int> S; 13 inline int lowbit(int x){return x&(-x);} 14 inline void update(int x,int v){for(;x;x-=lowbit(x)) tr[x]+=v;} 15 inline int query(int x){int ans=0; for(;x<=n;x+=lowbit(x)) ans+=tr[x];return ans;} 16 inline int getfa(int x){return fa[x]=((fa[x]==x)?x:getfa(fa[x]));}
17 inline void merge(int x,int y){ 18 int xx=getfa(x),yy=getfa(y); 19 if(xx==yy) return; 20 update(siz[xx],-1);--t[siz[xx]];if(!t[siz[xx]]) S.erase(S.find(siz[xx])); 21 update(siz[yy],-1);--t[siz[yy]];if(!t[siz[yy]]) S.erase(S.find(siz[yy])); 22 ++t[siz[xx]+siz[yy]]; update(siz[xx]+siz[yy],1); if(t[siz[xx]+siz[yy]]==1) S.insert(siz[xx]+siz[yy]); 23 if(siz[xx]>siz[yy]) swap(xx,yy); 24 fa[xx]=yy; siz[yy]+=siz[xx]; 25 --r; 26 } 27 namespace WSN{ 28 inline short main(){ 29 n=read(); m=read(); t[1]=r=n; 30 for(int i=1;i<=n;++i) siz[i]=1,fa[i]=i; 31 update(1,n); S.insert(1); 32 while(m--){ 33 int opt=read(); 34 if(opt==1){ 35 int x=read(),y=read(); 36 merge(x,y); 37 continue; 38 } 39 if(opt==2){ 40 int c=read(); 41 if(!c) printf("%lld\n",r*(r-1)/2); 42 else{ 43 int ans=0; 44 for(auto val:S) if(val+c<=n){ 45 int res=query(val+c); 46 ans+=res*t[val]; 47 if(!res) break; 48 }else break; 49 printf("%lld\n",ans); 50 } 51 continue; 52 } 53 } 54 return 0; 55 } 56 } 57 signed main(){return WSN::main();}
View Code

T2 Cicada與排序

考場上居然在大力的剛一道不可做的題,針不戳。

一直在想如何魔改貴並排序的板子,考後看題解發現是$dp$,果然看題沒思路就往$dp$想

記錄$rk_{i,j}$表示原陣列第$i$個數在排列後出現在$j$的概率,那麼我們用這個就可以求出答案

其他詳情見學長部落格

太可惡了,題根本改不完,昨天四道題兩道沒改。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 //出現在mid一側的相同數期望相同
 4 using namespace std;
 5 inline int read(){
 6     int x=0,f=1; char ch=getchar();
 7     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 8     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
 9     return x*f;
10 }
11 inline void write(int x){
12     char ch[20]; int len=0;
13     if(x<0) x=~x+1, putchar('-');
14     do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x);
15     for(int i=len-1;i>=0;--i) putchar(ch[i]); putchar(' ');
16 }
17 #define yi 0
18 #define er 1
19 const int p=998244353,NN=1005,inv=499122177;
20 int n,a[NN],dp[NN][NN][2],rk[NN][NN];
21 int lt[NN],rt[NN],zz[NN];
22 vector<int> vl[NN],vr[NN];
23 void merge_sort(int    le, int    ri){
24     if(le==ri) return; int mi=(le+ri)>>1;
25     merge_sort(le,mi); merge_sort(mi+1,ri);
26     for(int i=le;i<=mi;i++) ++lt[a[i]],vl[a[i]].push_back(i);
27     for(int i=mi+1;i<=ri;i++) ++rt[a[i]],vr[a[i]].push_back(i);
28     for(int i=1;i<=1000;i++){
29         for(int l=0;l<=lt[i];l++) for(int r=0;r<=rt[i];r++) dp[l][r][0]=dp[l][r][1]=0;
30         dp[0][0][0]=1;
31         for(int l=0;l<=lt[i];l++) for(int r=0;r<=rt[i];r++){
32             if(l!=lt[i]&&r!=rt[i]){
33                 (dp[l+1][r][yi]+=(dp[l][r][yi]+dp[l][r][er])*inv%p)%=p;
34                 (dp[l][r+1][er]+=(dp[l][r][yi]+dp[l][r][er])*inv%p)%=p;
35             }
36             else if(l!=lt[i]) (dp[l+1][r][yi]+=dp[l][r][yi]+dp[l][r][er])%=p;
37             else if(r!=rt[i]) (dp[l][r+1][er]+=dp[l][r][yi]+dp[l][r][er])%=p;
38         }
39         for(int j=0;j<lt[i];j++){
40             for(int k=1;k<=lt[i];k++) for(int r=0;r<=rt[i];r++)
41                 (zz[k+r]+=rk[vl[i][j]][k]*dp[k][r][0])%=p;
42             for(int k=1;k<=lt[i]+rt[i];k++) rk[vl[i][j]][k]=zz[k],zz[k]=0;
43         }
44         for(int j=0;j<rt[i];j++){
45             for(int k=1;k<=rt[i];k++) for(int l=0;l<=lt[i];l++)
46                 (zz[k+l]+=rk[vr[i][j]][k]*dp[l][k][1])%=p;
47             for(int k=1;k<=lt[i]+rt[i];k++) rk[vr[i][j]][k]=zz[k],zz[k]=0;
48         }
49         vl[i].clear(),vr[i].clear(); lt[i]=rt[i]=0;
50     }
51 }
52 void bu_shan_hui_si(){
53     freopen("1.in","r",stdin);
54     freopen("1.out","w",stdout);
55 }
56 namespace WSN{
57     inline short main(){
58         n=read(); for(int i=1;i<=n;i++)    a[i]=read();
59         for(int i=1;i<=n;i++) rk[i][1]=1;
60         merge_sort(1,n);
61         for(int i=1;i<=n;i++) ++lt[a[i]];
62         for(int i=0;i<=1000;i++) lt[i]+=lt[i-1];
63         for(int i=1;i<=n;i++){
64             int ans=0;
65             for(int j=1;j<=lt[a[i]]-lt[a[i]-1];j++) (ans+=rk[i][j]*j%p)%=p;
66             write(ans+lt[a[i]-1]);
67         }
68         return 0;
69     }
70 }
71 signed main(){return WSN::main();}
View Code

T3 Cicada拿衣服

亂搞題,$AC$全靠勇氣。一波巧妙判斷搞定$O(n^2)$複雜度

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 #define lid (id<<1)
 5 #define rid (id<<1|1)
 6 inline int read(){
 7     int x=0,f=1; char ch=getchar();
 8     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 9     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
10     return x*f;
11 }
12 inline void write(int x){
13     char ch[20]; int len=0;
14     if(x<0) x=~x+1, putchar('-');
15     do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x);
16     for(int i=len-1;i>=0;--i) putchar(ch[i]); putchar(' ');
17 }
18 const int NN=1e7+5;
19 int n,k,a[NN];
20 struct SNOWtree{
21     int ll[NN<<2],rr[NN<<2];
22     int maxn[NN<<2],laz[NN<<2];
23     inline void pushup(int id){
24         if(ll[id]==rr[id]) return;
25         maxn[id]=max(maxn[lid],maxn[rid]);
26     }
27     inline void pushdown(int id){
28         if(!laz[id]||ll[id]==rr[id]) return;
29         laz[lid]=max(laz[lid],laz[id]);
30         laz[rid]=max(laz[rid],laz[id]);
31         maxn[lid]=max(maxn[lid],laz[id]);
32         maxn[rid]=max(maxn[rid],laz[id]);
33         laz[id]=0;
34     }
35     void build(int id,int l,int r){
36         ll[id]=l;rr[id]=r; maxn[id]=-1;
37         if(l==r) return;int mid=l+r>>1;
38         build(lid,l,mid); build(rid,mid+1,r);
39     }
40     void update(int id,int l,int r,int val){
41         if(l<=ll[id]&&rr[id]<=r){
42             maxn[id]=max(maxn[id],val);
43             laz[id]=max(laz[id],val);return;
44         }pushdown(id);int mid=ll[id]+rr[id]>>1;
45         if(l<=mid) update(lid,l,r,val);
46         if(r>mid) update(rid,l,r,val);
47         pushup(id);
48     }
49     int query(int id,int pos){
50         if(ll[id]==rr[id]) return maxn[id];
51         pushdown(id); int mid=ll[id]+rr[id]>>1;    
52         if(pos<=mid) return query(lid,pos);
53         else return query(rid,pos);
54     }
55 }tr;
56 namespace WSN{
57     inline short main(){
58         n=read();k=read();
59         for(register int i=1;i<=n;++i) a[i]=read();
60         tr.build(1,1,n); int mx,mn,s1,s2,now;
61         for(register int l=1;l<=n;++l){
62             mx=mn=s1=s2=a[l]; now=0;
63             for(register int r=l;r<=n;++r){
64                 mx=max(mx,a[r]);
65                 mn=min(mn,a[r]);
66                 s1=s1|a[r];
67                 s2=s2&a[r];
68                 if(mn+s1-mx-s2>=k) now=r;
69                 else if(r-l+1>100&&n>30000) break;
70             }
71             if(now) tr.update(1,l,now,now-l+1);
72         }
73         for(register int i=1;i<=n;++i){
74             int ans=tr.query(1,i);
75             write(ans);
76         }
77         return 0;
78     }
79 }
80 signed main(){return WSN::main();}
View Code

說說正解,$ST$表+連結串列$list$,比較離譜,頭一次接觸連結串列這種東西

也是第一次在程式碼裡面使用這麼多的指標,

將柿子倒位置,發現$MAX-MIN$較好維護,直接用$ST$表處理即可

對於$OR-AND$,考慮按位運算的性質,一個範圍內的這個值不改變當且僅當同一位置上的數都一樣

我們考慮找到這樣的一段區間,然後就直接線上段樹上插入長度,最後出解即可

使用連結串列維護這段區間,這段區間合法與否可用是否大於$k$來判斷。二分判斷

注意邊界是上一個連結串列最右端+1。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 inline int read(){
 5     int x=0,f=1; char ch=getchar();
 6     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 7     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
 8     return x*f;
 9 }
10 inline void write(int x){
11     char ch[20]; int len=0;
12     if(x<0) x=~x+1, putchar('-');
13     do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x);
14     for(int i=len-1;i>=0;--i) putchar(ch[i]); putchar(' ');
15 }
16 const int NN=1e6+5;
17 int n,k,a[NN],lgn,lg[NN],mn[27][NN],mx[27][NN];
18 struct SNOW{int r,mo,ma;};list<SNOW> lis;
19 inline int qmx(int l,int r){int k=lg[r-l+1];return max(mx[k][l],mx[k][r-(1<<k)+1]);}
20 inline int qmn(int l,int r){int k=lg[r-l+1];return min(mn[k][l],mn[k][r-(1<<k)+1]);}
21 inline bool check(list<SNOW>::iterator it,int l,int r){return it->mo-it->ma+qmn(l,r)-qmx(l,r)>=k;}
22 inline void ST_table(){
23     for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
24     for(int j=1;j<=lgn;j++)    for(int i=1,t=(1<<j-1);i+(1<<j)-1<=n;i++)
25         mn[j][i]=min(mn[j-1][i],mn[j-1][i+t]),mx[j][i]=max(mx[j-1][i],mx[j-1][i+t]);
26 }
27 struct SNOWtree{
28     #define lid (id<<1)
29     #define rid (id<<1|1)
30     int ans[NN<<2];
31     inline void insert(int id,int l,int r,int L,int R,int val){
32         if(L<=l&&r<=R){ans[id]=max(ans[id],val);return;}
33         int mid=l+r>>1;
34         if(L<=mid) insert(lid,l,mid,L,R,val);
35         if(R>mid) insert(rid,mid+1,r,L,R,val);
36     }
37     inline void print(int id,int l,int r){
38         if(l==r){write(ans[id]);return;}
39         int mid=l+r>>1;
40         ans[lid]=max(ans[lid],ans[id]);
41         ans[rid]=max(ans[rid],ans[id]);
42         print(lid,l,mid); print(rid,mid+1,r);
43     }
44 }tr;
45 namespace WSN{
46     inline short main(){
47         n=read();k=read(); lgn=log(n)/log(2);
48         for(int i=1;i<=n;i++) mx[0][i]=mn[0][i]=a[i]=read();
49         ST_table(); memset(tr.ans,-1,sizeof(tr.ans));
50         for(int R=1;R<=n;R++){
51             for(auto it=lis.begin();it!=lis.end();++it) it->mo|=a[R],it->ma&=a[R];
52             lis.push_back((SNOW){R,a[R],a[R]});
53             for(auto itl=lis.begin(),itr=itl;++itr!=lis.end();)
54                 if( (itl->mo-itl->ma) == (itr->mo-itr->ma) )
55                     lis.erase(itl),itl=itr;
56                 else ++itl;
57             for(auto it=lis.begin();it!=lis.end();++it) if(check(it,it->r,R)){
58                 int ll=1,rr=it->r,ans=0;
59                 if(it!=lis.begin()) ll=(--it)->r+1,++it;
60                 while(ll<=rr){
61                     int mid=ll+rr>>1;
62                     if(check(it,mid,R)) rr=mid-1,ans=mid;
63                     else ll=mid+1;
64                 }
65                 tr.insert(1,1,n,ans,R,R-ans+1);
66                 break;
67             }
68         }
69         tr.print(1,1,n);
70         return 0;
71     }
72 }
73 signed main(){return WSN::main();}
View Code