第一輪結束了。總榜排名$rk8$在本校中$rk4$
考慮上$csp-s$的分數的話,妥妥退役。
在我前面的我翻不動,在我後面的也能隨便把我翻了,結果就沒什麼好說的。
最近狀態還是不好。抓緊調整吧,沒有時間了。
越發濃郁的文化課味道
今天的話,上來先看的$T2$,$52pts$貌似比較簡單,思路大概知道,先放了。
然後看的是$T1$發現就是一個簡單的線段樹優化$dp$,於是就開始寫,沒寫多久寫完了過樣例。
$T3$寫的樹套樹,寫了一大攤調了好半天。過樣例了。這時候看起來還挺順,這時候是$10:00$
然後開始上對拍,拍$T1$不出錯,很開心,自信估分$100$
然後接着拍$T3$,然而最開始思路是假的,被對拍幹掉了,$10:30$拍出錯,然後想了半天怎麼改(最開始一直以爲只是寫掛了
沒想出來,期望得分剩下$40$。後來教練突然刪了一檔部分分,期望只剩下$20$。
然後十分憤怒,於是就拼命改,想正解,然後發現了問題再一頓猛改,過了對拍,這時候是$11:30$
這時候已經考慮到了內存問題,對於$3 \times 10^5$的數據範圍十分無奈。估分$20$(然而不知道是數據水還是常數小並沒有$MLE$)
最後剩下很少的時間給$T2$,把原有思路一頓寫然後過了樣例,很開心,本場估分$248$。
然而$T1,2$各炸了一個細節,分別掛了$30,48$分。人都掛沒了
$T1$的對拍的數據生成寫的不好,$Dyyb$大神的對拍一組數據就把我卡了我自己$AC$了$50000$組。自閉了。
T1:旅遊
大意:數軸,$x$出發最終要走到$y$,每次最多從$p$跳到$p+z$,每跳一次花費$a$。有$n$個特殊點,跳到特殊點上會有$w_i$收益。最大化總收益。$n \le 10^5,1 \le x,y,z,a \le 10^9$
首先最簡單的$O(n^2)dp$不用說。然後當然可以套用$O(1000n)$的亂搞。可以得到$90pts$的好成績,要比寫掛的正解舒服的多。(數據水死算了
我們看一下轉移式子,大概是$dp_i=max(dp_j+w_i + \lceil \frac{p_i-p_i}{z} \rceil \times a)$
把上取整拆開,發現$a$的係數只與$\lfloor \frac{p_i}{z} \rfloor,\lfloor \frac{p_j}{z} \rfloor,p_i \mod z <? p_j \mod z$
對於最後一部分,如果小於的話那麼要額外產生$a$代價。所以線段樹下標按照$p_i \mod z$查詢的時候區間加就可以
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 unordered_map<int,int>M; 5 const int S=4000007;const ll Inf=-1e18; 6 int x,y,z,n,p[S],w[S],r[S],tl,tr; ll dp[S],mx[S],a,lz[S]; 7 bool cmp(int x,int y){return x%z<y%z||((x-y)%z==0&&x<y);} 8 #define lc p<<1 9 #define rc lc|1 10 #define md (L+R>>1) 11 void up(int p){mx[p]=max(mx[lc],mx[rc]);} 12 void build(int p=1,int L=tl,int R=tr){ 13 if(L==R){mx[p]=r[L]==x?dp[tl]+x/z*a:Inf;return;} 14 build(lc,L,md);build(rc,md+1,R); up(p); 15 } 16 void mdf(int x,ll v,int p=1,int L=tl,int R=tr){ 17 if(L==R){mx[p]=v;return;} 18 if(x<=md)mdf(x,v,lc,L,md);else mdf(x,v,rc,md+1,R); 19 up(p); 20 } 21 ll ask(int l,int r,int p=1,int L=tl,int R=tr){ 22 if(l<=L&&R<=r)return mx[p]; 23 return max(l<=md?ask(l,r,lc,L,md):Inf,r>md?ask(l,r,rc,md+1,R):Inf); 24 } 25 int main(){ 26 freopen("tourist.in","r",stdin);freopen("tourist.out","w",stdout); 27 scanf("%d%d%d%lld%d",&x,&y,&z,&a,&n); tl=1,tr=n; 28 for(int i=1;i<=n;++i)scanf("%d%d",&p[i],&w[i]),r[i]=p[i]; 29 if(p[n]!=y)p[++tr]=y,r[tr]=y; if(p[1]!=x)p[--tl]=x,r[tl]=x; 30 sort(r+tl,r+1+tr,cmp); 31 for(int i=tl;i<=tr;++i)M[r[i]]=i; 32 dp[tl]=w[tl]; build(); 33 for(int i=tl+1;i<=tr;++i){ 34 int P=lower_bound(r+tl,r+tr+1,p[i]%z,cmp)-r; 35 ll q=max(P==tl?Inf:ask(tl,P-1)-a,ask(P,tr))+w[i]; 36 dp[i]=-p[i]/z*a+q; mdf(M[p[i]],q); 37 }printf("%lld",dp[tr]); 38 }
T2:寶石
大意:$n$石頭,要求其中至少有$m$對顏色一樣的(每一個石頭只參與一次配對)。顏色有$d$種且石頭有序。求所有情況中有多少種滿足要求。$n,m \le 10^9,d \le 10^5$
首先很明顯的是,設$A$表示出現次數爲奇數的顏色數,那麼$n-2m \ge A$。
我們考慮指數型生成函數,首先出現次數不限奇偶的話生成函數就是$\sum\limits_{i=0}^{\infty} \frac{x^i}{i!} = e^x$
可以得到,出現次數爲奇數的就是$F(x)=\frac{e^x-e^{-x}}{2}$
我們設$g_i$是至少有$i$種顏色出現了奇數次,那麼$g_i=\binom{d}{i} n! [x^n]e^{x(n-i)}(\frac{e^x-e^{-x}}{2})^i$
後面那玩意二項式定理展開並且把$e^x$都合併起來得到$n!\binom{d}{i} \frac{1}{2^i} [x^n] \sum\limits_{j=0}^{i} \binom{i}{j} (-1)^{i-j} e^{x(d+2j-2i)}$
我們知道$[x^n]e^{xm}=\frac{m^n}{n!}$。所以上面這個式子就會變成卷積形式。然後再二項式反演得到恰好形式就可以了。$O(d\log d)$
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int S=1<<19,mod=998244353; 5 int len=1,rev[S],fac[S],inv[S],n,m,d,A[S],f[S],ans; 6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 7 int mo(int x){return x>=mod?x-mod:x;} 8 void NTT(int*a,int op=1){ 9 for(int i=0,x;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]); 10 for(int i=1;i<len;i<<=1)for(int j=0,w=qp(3,(mod-1)/2/i*op+mod-1);j<len;j+=i<<1) 11 for(int k=j,t=1,x,y;k<j+i;++k,t=1ll*t*w%mod) 12 x=a[k],y=1ll*t*a[k+i]%mod,a[k]=mo(x+y),a[k+i]=mo(x+mod-y); 13 if(op==-1)for(int i=0,iv=qp(len,mod-2);i<len;++i)a[i]=1ll*a[i]*iv%mod; 14 } 15 int main(){ 16 freopen("jewel.in","r",stdin);freopen("jewel.out","w",stdout); 17 scanf("%d%d%d",&d,&n,&m); 18 for(int i=fac[0]=1;i<=d;++i)fac[i]=fac[i-1]*1ll*i%mod; 19 A[d]=inv[d]=qp(fac[d],mod-2); 20 for(int i=d-1;~i;--i)A[i]=inv[i]=inv[i+1]*(i+1ll)%mod; 21 for(int i=0;i<=d;++i)f[i]=(i&1?mod-1ll:1ll)*qp(d-i-i+mod,n)%mod*A[i]%mod; 22 while(len<=d+d)len<<=1; 23 for(int i=0;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0); 24 NTT(A);NTT(f);for(int i=0;i<len;++i)f[i]=1ll*f[i]*A[i]%mod;NTT(f,-1); 25 for(int i=0;i<=d;++i)f[i]=1ll*f[i]*qp(2,mod-1-i)%mod*fac[i]%mod*fac[d]%mod*inv[d-i]%mod,A[i]=(i&1?mod-1ll:1ll)*inv[i]%mod; 26 for(int i=d+1;i<len;++i)f[i]=A[i]=0; 27 reverse(f,f+d+1); 28 NTT(A);NTT(f);for(int i=0;i<len;++i)f[i]=1ll*f[i]*A[i]%mod;NTT(f,-1); 29 reverse(f,f+d+1); 30 for(int i=min(d,n-m-m);i>=0;--i)ans=(ans+f[i]*1ll*inv[i])%mod; 31 printf("%d",ans); 32 }
T3:線段
大意:有$n+1$個點,$(i,i+1)$有邊相連。支持:反轉一條邊的狀態(連變成不連或者不連變成連),詢問$x,y$兩個點在多少個歷史版本中是聯通的。$n,q \le 3 \times 10^5$
題意也就是問有多少個歷史狀態滿足區間$[x,y-1]$全都是$1$。
每次修改只會使原來不聯通現在聯通的,或者反過來。
假如修改的點是$x$,$x$所在的極長聯通段是$[l,r]$,那麼就會分別使得 詢問左端點在$[l,x]$右端點在$[x,r]$的詢問答案增加或減少當前時間。
矩形加,單點詢問。經典二維數點。直接樹套樹(內存有點危險但是莫名其妙過去了)。還是$CDQ$比較好。
但是我都寫了,那就不可能改了(還寫的那麼麻煩
1 #include<cstdio> 2 int max(int a,int b){return a>b?a:b;} 3 const int S=300005,_=20000005; 4 char s[S],o[9];int n,q,mx[S<<2],T=1,rt[S<<2],w[_],Lc[_],Rc[_],pc,lz[S<<2]; 5 #define lc p<<1 6 #define rc lc|1 7 #define md (L+R>>1) 8 void build(int p=1,int L=1,int R=n){ 9 if(L==R){mx[p]=s[L]=='1'?0:S;return;} 10 build(lc,L,md);build(rc,md+1,R); mx[p]=max(mx[lc],mx[rc]); 11 } 12 void down(int p){if(lz[p])lz[lc]=lz[p],lz[rc]=lz[p],mx[lc]=mx[p],mx[rc]=mx[p],lz[p]=0;} 13 void up(int p){mx[p]=max(mx[lc],mx[rc]);} 14 void segset(int l,int r,int p=1,int L=1,int R=n){ 15 if(l<=L&&R<=r){lz[p]=mx[p]=T;return;} 16 down(p);if(l<=md)segset(l,r,lc,L,md);if(r>md)segset(l,r,rc,md+1,R); up(p); 17 } 18 int Max(int l,int r,int p=1,int L=1,int R=n){ 19 if(l<=L&&R<=r)return mx[p];down(p); 20 return max(l<=md?Max(l,r,lc,L,md):0,r>md?Max(l,r,rc,md+1,R):0); 21 } 22 void Upd(int x,int p=1,int L=1,int R=n){ 23 if(L==R){mx[p]=mx[p]==S?T:S;return;} down(p); 24 if(x<=md)Upd(x,lc,L,md);else Upd(x,rc,md+1,R); up(p); 25 } 26 int getl(int x,int p=1,int L=1,int R=n){ 27 if(mx[p]!=S)return L; 28 if(L==R)return R+1; 29 down(p); 30 if(x<=md)return getl(x,lc,L,md); 31 int y=getl(x,rc,md+1,R);return y==md+1?getl(md,lc,L,md):y; 32 } 33 int getr(int x,int p=1,int L=1,int R=n){ 34 if(mx[p]!=S)return R; 35 if(L==R)return L-1; 36 down(p); 37 if(x>md)return getr(x,rc,md+1,R); 38 int y=getr(x,lc,L,md);return y==md?getr(md+1,rc,md+1,R):y; 39 } 40 void add(int&p,int l,int r,int v,int L=1,int R=n){ 41 if(!p)p=++pc;//printf("add:%d %d %d %d %d %d\n",p,l,r,v,L,R); 42 if(l<=L&&R<=r){w[p]+=v;return;} 43 if(l<=md)add(Lc[p],l,r,v,L,md); if(r>md)add(Rc[p],l,r,v,md+1,R); 44 } 45 int ask(int p,int x,int L=1,int R=n){//printf("ask:%d %d %d %d %d\n",p,x,L,R,w[p]); 46 if(!p||L==R)return w[p]; 47 return w[p]+(x<=md?ask(Lc[p],x,L,md):ask(Rc[p],x,md+1,R)); 48 } 49 void Add(int l,int r,int _l,int _r,int v,int p=1,int L=1,int R=n){ 50 if(l<=L&&R<=r)return add(rt[p],_l,_r,v); 51 if(l<=md)Add(l,r,_l,_r,v,lc,L,md);if(r>md)Add(l,r,_l,_r,v,rc,md+1,R); 52 } 53 int Sum(int x,int y,int p=1,int L=1,int R=n){return ask(rt[p],y)+(L!=R?(x<=md?Sum(x,y,lc,L,md):Sum(x,y,rc,md+1,R)):0);} 54 int main(){ 55 freopen("segment.in","r",stdin);freopen("segment.out","w",stdout); 56 scanf("%d%d%s",&n,&q,s+1); 57 build(1,1,n); 58 for(int x,l,r;T<=q;++T){ 59 scanf("%s%d",o,&x); 60 if(o[0]=='t'){ 61 l=getl(x),r=getr(x); 62 if(s[x]=='1')Add(l,x,x,r,T-Max(l,r));//,printf("operate:%d %d %d %d %d\n",l,x,x,r,T-Max(l,r)); 63 else{ 64 l=getl(x-1);r=getr(x+1); 65 if(l<x)Add(l,x-1,l,x-1,T-Max(l,x-1)),segset(l,x-1);//,printf("operate:%d %d %d %d %d\n",l,x-1,l,x-1,T-Max(l,x-1)); 66 if(r>x)Add(x+1,r,x+1,r,T-Max(x+1,r)),segset(x+1,r);//,printf("operate:%d %d %d %d %d\n",x+1,r,x+1,r,T-Max(x+1,r)); 67 } 68 s[x]^=1;Upd(x); 69 }else{ 70 scanf("%d",&r),r--; 71 int y=Max(x,r); 72 if(y==S)printf("%d\n",Sum(x,r)); 73 else printf("%d\n",Sum(x,r)+T-Max(getl(x),getr(r))); 74 } 75 } 76 }