【HNOI2019】部分题简要题解
題意懶得寫(xiě)了
LOJ
Day 1 T1 魚(yú)
個(gè)人做法比較獵奇,如果有哪位大佬會(huì)證明能分享一下的話感激不盡。
題解:枚舉魚(yú)尾和魚(yú)身的交點(diǎn)D,將所有其他點(diǎn)按照到D的距離排序,距離相同的分一組。
感性的理解,對(duì)于每個(gè)點(diǎn)D,暴力枚舉距離相等的點(diǎn)對(duì)(B,C)。這樣總的數(shù)量不會(huì)很多。感覺(jué)仍然是\(O(n^2)\)級(jí)別的。
那么我們對(duì)枚舉的D,將所有的點(diǎn)對(duì)的中垂射線和點(diǎn)按照極角排序,掃一圈就能得到答案了。魚(yú)尾的部分也是利用掃描線,用叉積判斷可能會(huì)有問(wèn)題(轉(zhuǎn)過(guò)了180度),那么我們可以將其倍長(zhǎng),用極角的值來(lái)判即可。
精度要求比較高。
奇丑無(wú)比...
#include <bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;++i) #define fod(i,a,b) for(int i=a;i>=b;--i) #define N 2005 #define M 1000005 #define LL long long #define LT long double using namespace std; int n; LL ans; //geometry long double sqr(LL x) {LT v=x;return v*v; } namespace geometry {const long double pi=acos(-1);const long double eps=1e-15;struct node{LL x,y;node(LL _x=0,LL _y=0){x=_x,y=_y;}};node operator +(node x,node y) {return node(x.x+y.x,x.y+y.y);}node operator -(node x,node y) {return node(x.x-y.x,x.y-y.y);}long double angel(node x){long double v=atan2(x.y,x.x);if(v<0) v+=2*pi;return v;}LT dis2(node x,node y) {return sqr(x.x-y.x)+sqr(x.y-y.y);}LT crs(node x,node y) {return (LT)x.x*(LT)y.y-(LT)x.y*(LT)y.x;} } using namespace geometry;node d[N],a[N],dw[M]; LL c1[N],cnt[N]; LT ds[N]; int n1,px[N],fr[N],l,le; vector<int> pt[N];long double ag(int x) {if(x>l) return angel(d[px[x]])+2*pi;else return angel(d[px[x]]); } int pw(node x) {if(x.x>=0) return (x.y>=0)?1:4;else return (x.y>=0)?2:3; } bool cmp(int x,int y) {return ds[x]<ds[y];} bool cmp2(node x,node y) {int fx=pw(x),fy=pw(y);return (fx<fy||(fx==fy&&crs(x,y)>0)); } bool cmp3(int x,int y) {return cmp2(d[x],d[y]);} bool cmp4(node x,node y) {return dis2(x,node(0,0))<dis2(y,node(0,0));}bool eql(node x,node y) {return(!cmp2(x,y)&&!cmp2(y,x)); } int ed[N],mx,mn;LL query(int k) { l=0;fo(i,1,n) if(i!=k){d[++l]=a[i]-a[k];px[l]=l;ds[l]=dis2(a[i],a[k]);} sort(px+1,px+l+1,cmp);n1=0;fo(i,1,l) {if(i==1||abs(ds[px[i]]-ds[px[i-1]])>eps){cnt[++n1]=0;pt[n1].clear();}cnt[n1]++,pt[n1].push_back(px[i]),fr[px[i]]=n1;}if(l<5) return 0;le=0;fo(i,1,n1) {fo(j,0,cnt[i]-1){fo(k,j+1,cnt[i]-1){int u=pt[i][j],v=pt[i][k];if((d[u]+d[v]).x!=0||(d[u]+d[v]).y!=0) dw[++le]=d[u]+d[v];}}}memset(c1,0,sizeof(c1));sort(px+1,px+l+1,cmp3);sort(dw+1,dw+le+1,cmp2);int lst=0;fo(i,1,l) {if(i==1||!eql(d[px[i]],d[px[i-1]])){if(lst!=0) {sort(px+lst,px+i,cmp);fo(j,lst,i-1) ed[j]=i-1;}lst=i;}}if(lst!=0) {sort(px+lst,px+l+1,cmp);fo(j,lst,l) ed[j]=l;}lst=0;fo(i,1,le){if(i==1||!eql(dw[i],dw[i-1])){if(lst!=0) sort(dw+lst,dw+i,cmp4);lst=i;}}if(lst!=0) sort(dw+lst,dw+le+1,cmp4);int lp=1,rp=1;LL s1=0,sp=0;fo(i,1,l) px[i+l]=px[i];long double upg=angel(dw[1])+pi/2,dpg=angel(dw[1])+3*pi/2;while(lp<=2*l&&ag(lp)<upg+eps) lp++;rp=lp;while(rp<=2*l&&ag(rp)<dpg) rp++;for(int j=lp;j<rp;j++){sp-=(LL)c1[fr[px[j]]]*(LL)(c1[fr[px[j]]]-1);c1[fr[px[j]]]++;sp+=(LL)c1[fr[px[j]]]*(LL)(c1[fr[px[j]]]-1);}int j=1,ef=0;while(j<=l&&cmp2(d[px[j]],dw[1])) j++;if(j<=l&&eql(d[px[j]],dw[1])) ef=ed[j];else ef=0;LT di=dis2(dw[1],node(0,0));while(j<=ef&&di>=(LT)4*ds[px[j]]) j++;if(ef!=0) s1=s1+sp*(LL)2*(LL)(ef-j+1);mx=max(mx,le),mn=max(mn,n1);fo(i,2,le){if(i==1||!eql(dw[i],dw[i-1])){upg=angel(dw[i])+pi/2,dpg=angel(dw[i])+3*pi/2;while(rp<=2*l&&ag(rp)<dpg-eps){sp-=(LL)c1[fr[px[rp]]]*(LL)(c1[fr[px[rp]]]-1);c1[fr[px[rp]]]++;sp+=(LL)c1[fr[px[rp]]]*(LL)(c1[fr[px[rp]]]-1);rp++;}while(lp<=2*l&&ag(lp)<upg+eps) {sp-=(LL)c1[fr[px[lp]]]*(LL)(c1[fr[px[lp]]]-1);c1[fr[px[lp]]]--;sp+=(LL)c1[fr[px[lp]]]*(LL)(c1[fr[px[lp]]]-1);lp++;}while(j<=l&&cmp2(d[px[j]],dw[i])) j++;if(j<=l&&eql(d[px[j]],dw[i])) ef=ed[j];else ef=0;}LT di=dis2(dw[i],node(0,0));while(j<=ef&&di>=(LT)4*ds[px[j]]) j++;if(ef!=0) s1=s1+sp*(LL)2*(LL)(ef-j+1);} return s1; } int main() {cin>>n;fo(i,1,n) scanf("%lld%lld\n",&a[i].x,&a[i].y);ans=0;fo(i,1,n) {ans+=query(i);}printf("%lld\n",ans); }Day 1 T3 多邊形
題解:觀察樣例可以發(fā)現(xiàn),最終狀態(tài)一定是n-3條邊都與n號(hào)點(diǎn)相連。
那么次數(shù)最少的操作一定每一次都是要連到n的
進(jìn)一步可以發(fā)現(xiàn),某些邊的操作一定在某條邊之后,先后關(guān)系可以構(gòu)成一個(gè)森林。
操作次數(shù)就是森林點(diǎn)數(shù),方案數(shù)就是森林的拓?fù)湫蚍N數(shù)。
現(xiàn)在提前給出一些操作,觀察這些操作在樹(shù)上的變化,要么直接刪去一個(gè)點(diǎn),要么改變一些父子關(guān)系,改變的個(gè)數(shù)只有常數(shù)個(gè),直接計(jì)算這些點(diǎn)對(duì)答案的影響即可。
用map來(lái)存標(biāo)號(hào),時(shí)間復(fù)雜度\(O(n\log n)\)
#include <bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;++i) #define fod(i,a,b) for(int i=a;i>=b;--i) #define N 100005 #define mo 1000000007 #define LL long long using namespace std; int mx[N],mi[N],n,m,n1,tp,ft[N],fn[N],t[N][2],ap[N][2],sz[N]; map<int,int> h[N]; LL f[N],js[N],ns[N],ny[N]; bool bz[N];LL C(int n,int m) {if(n<m) return 0;return js[n]*ns[m]%mo*ns[n-m]%mo; } LL nC(int n,int m) {if(n<m) return 0;return ns[n]*js[m]%mo*js[n-m]%mo; }void dfs(int k) {if(!k) return;dfs(t[k][0]),dfs(t[k][1]);sz[k]+=sz[t[k][0]]+sz[t[k][1]];f[k]=f[t[k][0]]*f[t[k][1]]%mo*C(sz[k]-1,sz[t[k][0]])%mo; }LL ksm(LL k,LL n) {LL s=1;for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;return s; } int main() {freopen("polygon.in","r",stdin);freopen("polygon.out","w",stdout);cin>>tp; cin>>n;js[0]=ns[0]=js[1]=ns[1]=ny[1]=1;fo(i,2,n) js[i]=js[i-1]*(LL)i%mo,ny[i]=(-ny[mo%i]*(LL)(mo/i)%mo+mo)%mo;fo(i,2,n) ns[i]=ns[i-1]*ny[i]%mo;memset(mi,107,sizeof(mi));fo(i,1,n-3){int x,y;scanf("%d%d",&x,&y);if(x>y) swap(x,y);if(y==n) {bz[i]=1;continue;}ap[i][0]=x,ap[i][1]=y;h[x][y]=i,h[y][x]=i;mx[x]=max(mx[x],y),mx[y]=max(mx[y],x);mi[x]=min(mi[x],y),mi[y]=min(mi[y],x);}fo(i,1,n-3){int x=ap[i][0],y=ap[i][1];if(bz[i]) continue;n1++,sz[i]=1;if(mx[x]>y){int w=(*h[x].upper_bound(y)).second;if(bz[w]) continue;ft[i]=w,fn[i]=0,t[w][0]=i;}else if(mi[y]<x){map<int,int>::iterator it=h[y].find(x);it--;int w=(*it).second;if(bz[w]) continue;ft[i]=w,fn[i]=1,t[w][1]=i;}}f[0]=1;LL ans=1,sp=0;fo(i,1,n-3) if(!bz[i]&&!ft[i]) {dfs(i);sp+=sz[i];ans=ans*f[i]%mo*C(sp,sz[i])%mo;}printf("%d",n1);if(tp) printf(" %lld",ans);printf("\n");cin>>m;fo(i,1,m){int x,y,w;scanf("%d%d",&x,&y);w=h[x][y];int n2=n1;LL s1=ans;if(!ft[w]){n2--;s1=s1*nC(sp,sz[w])%mo*ksm(f[w],mo-2)%mo;s1=s1*C(sp-1-sz[t[w][0]],sz[t[w][1]])%mo*f[t[w][1]]%mo;s1=s1*C(sp-1,sz[t[w][0]])%mo*f[t[w][0]]%mo;}else{int r=ft[w],p=fn[w];if(p!=0) printf("WA\n");s1=s1*ksm(f[r],mo-2)%mo;s1=s1*f[t[w][1]]%mo*f[t[r][1]]%mo*C(sz[t[w][1]]+sz[t[r][1]],sz[t[w][1]])%mo*f[t[w][0]]%mo*C(sz[r]-1,sz[t[w][0]])%mo;}printf("%d",n2);if(tp) printf(" %lld",s1);printf("\n");} }Day 2 T1 校園旅行
題解:考慮一個(gè)暴力DP,\(f[x][y]\)表示x,y是否能通過(guò)回文串到達(dá),枚舉兩邊的出邊,按照BFS的順序轉(zhuǎn)移,時(shí)間復(fù)雜度\(O(m^2)\)
考慮優(yōu)化,我們將邊分類,要么是連接不同顏色的邊,要么是連接相同顏色的邊。
只取出連接相同顏色的邊,考慮每一個(gè)連通塊,如果它是個(gè)二分圖,那么保留一棵生成樹(shù)即可(因?yàn)槠ヅ渑渲慌c奇偶性有關(guān),數(shù)量不同可以在一條邊反復(fù)橫跳滿足)。如果不是,那么奇偶性可以改變,可以任意匹配,那么保留一棵生成樹(shù),再連上一個(gè)自環(huán)表示奇環(huán)的情況。對(duì)于連接不同顏色的邊的聯(lián)通塊,它顯然是二分圖,直接保留生成樹(shù)即可。
連邊采用并查集,時(shí)間復(fù)雜度\(O(m\alpha(n))\)
這樣邊數(shù)也降為了\(O(n)\)級(jí)別,暴力DP的時(shí)間復(fù)雜度就變成了\(O(n^2)\)
Day 2 T2 白兔之舞
考慮枚舉走了i步,容易得出\(Ans_t=\sum\limits_{i=0}^{L}[(i-t)\%k==0]{L\choose i}W^i_{x,y}\)
根據(jù)單位根反演的公式\([n|k]={1\over n}\sum\limits_{i=0}^{n-1}\omega_n^{ki}\)
這里單位根我們找一個(gè)p的原根,然后取其(p-1)/k次作為K次單位根
代入,交換主體可得
\(Ans_t=\sum\limits_{j=0}^{k-1}\omega_k^{-jt}\sum\limits_{i=0}^{L}{L\choose i}\left(\omega_k^j\right)^iW^i_{x,y}\)
后面的部分用二項(xiàng)式定理就是\(\left(\omega_k^jW+I\right)^L_{x,y}\),枚舉j,用矩陣快速冪做
前面的\(\omega_k^{-jt}\),一個(gè)經(jīng)典的套路就是將\(jt\)拆成\({j+t\choose 2}-{j\choose 2}-{t\choose 2}\)
這樣剩下的就是一個(gè)卷積了,用任意模數(shù)NTT/MTT優(yōu)化即可。
時(shí)間復(fù)雜度\(O(n^3k\log L+k\log k)\)
Day 2 T3 序列
題解:可以發(fā)現(xiàn),最優(yōu)策略是將原序列分成若干段,每一段取它們的均值,并且均值應(yīng)該遞增。
進(jìn)一步的,記S[i]為a的1~i前綴和,我們將點(diǎn)\((i,S[i])\)放到平面上,求一個(gè)下凸殼,相鄰點(diǎn)的連線斜率就是這一段取的均值。用單調(diào)棧來(lái)做,就可以做到\(O(nm)\)了
考慮優(yōu)化,修改一個(gè)位置相當(dāng)于后綴的所有點(diǎn)整體下移/上移,我們預(yù)處理出前綴凸殼和后綴凸殼(單調(diào)棧構(gòu)成樹(shù)形結(jié)構(gòu),總的個(gè)數(shù)是\(O(n)\)的),現(xiàn)在相當(dāng)于做一個(gè)凸殼合并,我們?cè)谇熬Y凸殼上二分,再相應(yīng)的在后綴凸殼上二分找到最左的一個(gè)連接后滿足下凸性的點(diǎn),判斷前綴凸殼此時(shí)是否也是凸的相應(yīng)修改二分區(qū)間即可。(二分實(shí)際上可以用樹(shù)上倍增來(lái)實(shí)現(xiàn))
時(shí)間復(fù)雜度\(O(m\log ^2n)\)
實(shí)現(xiàn)上細(xì)節(jié)比較多
轉(zhuǎn)載于:https://www.cnblogs.com/BAJimH/p/10690153.html
總結(jié)
以上是生活随笔為你收集整理的【HNOI2019】部分题简要题解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《App后台开发运维与架构实践》第4章
- 下一篇: BZOJ3028食物——生成函数+泰勒展