NOI2015 題解

度過了神奇的學考時間,又回到了OI戰場上了...php

先刷了刷NOI2015,感受好像不是很難的樣子。node

 

Day1數據結構

T1 程序自動分析ide

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4195編碼

唔,給你相同的條件和不相同的條件,而後要你判斷是否是有矛盾...好像只要先把全部的相同的用某個神奇的數據結構弄到一塊兒,而後看一下要求不一樣的裏面有沒有在同一個聯通快裏的就好了...spa

這個神奇的數據結構,不就是並查集麼?...設計

而後因爲給的數字有點大,就須要離散化一下再並查集存起來...code

好良心的送分題blog

//BZOJ 4195
 #include<cstdio> #include<cstring> #include<algorithm>
 
using namespace std; const int maxn=1000010; inline int in(){ int x=0;char ch=getchar(); while(ch>'9' || ch<'0') ch=getchar(); while(ch<='9' && ch>='0') x=x*10+ch-'0',ch=getchar(); return x; } int n,sz,tp; int tmp[maxn<<1],Hash[maxn<<1]; int p[maxn<<1]; struct Node{ int x,y; bool tp; }s[maxn]; int Find_Hash(int x){ int l=1,r=tp,mid; while(r-l>1){ mid=(l+r)>>1; if(Hash[mid]>x) r=mid; else if(Hash[mid]<x) l=mid; else if(Hash[mid]==x) return mid; } if(Hash[l]==x) return l; return r; } bool cmp(const Node &A,const Node &B){ return A.tp>B.tp; } inline int find(int x){ int r=x,pre; while(p[r]!=r) r=p[r]; while(x!=r) pre=p[x],p[x]=r,x=pre; return r; } int main(){ #ifndef ONLINE_JUDGE freopen("prog.in","r",stdin); freopen("prog.out","w",stdout); #endif
 
    int Kase=in(); int fx,fy; while(Kase--){ n=in(); sz=0; for(int i=1;i<=n;i++){ s[i].x=in(),s[i].y=in(),s[i].tp=in(); tmp[++sz]=s[i].x,tmp[++sz]=s[i].y; } sort(tmp+1,tmp+sz+1); Hash[tp=1]=tmp[1]; for(int i=2;i<=sz;i++) if(tmp[i]!=tmp[i-1]) Hash[++tp]=tmp[i]; sort(s+1,s+n+1,cmp); for(int i=1;i<=tp;i++) p[i]=i; int rec=0,fool=0; for(int i=1;s[i].tp==1 && i<=n;i++){ fx=find(Find_Hash(s[i].x)),fy=find(Find_Hash(s[i].y)); p[fx]=fy; rec=i; } for(int i=rec+1;i<=n;i++){ fx=find(Find_Hash(s[i].x)),fy=find(Find_Hash(s[i].y)); if(fx==fy) {fool=1;puts("NO");break;} } if(!fool) puts("YES"); } return 0; }
View Code


T2 軟件包管理器get

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4196

給你一顆樹,剛開始全部節點都是0狀態,有兩種操做:

操做1:把點x到根的全部點所有改爲1,問須要改動幾個

操做2:把點x的子樹所有改爲0,問須要改動幾個

唔,一個問一條鏈,一個問一棵子樹,並且都是統一賦值,感受就差不告訴你要打樹鏈剖分了...而後改動幾個也就是問一下改動先後的樹上值的總和的變化...

好良心的模板題

//BZOJ 4196
 #include<cstdio> #include<cstring> #include<algorithm>
 
using namespace std; const int maxn=100010; inline int in(){ int x=0;char ch=getchar(); while(ch>'9' || ch<'0') ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } struct Node{ int data,next; }node[maxn]; #define now node[point].data
#define then node[point].next
 
struct Tree{ int sm,pt; Tree(){pt=-1;} }s[maxn*17]; int n,cnt,Idex; int head[maxn],Sz[maxn],Son[maxn],fa[maxn]; int id[maxn],el[maxn],hd[maxn]; void addedge(int u,int v){ node[cnt].data=v,node[cnt].next=head[u],head[u]=cnt++; } void dfs1(int x){ Sz[x]=1;Son[x]=-1; for(int point=head[x];point!=-1;point=then){ dfs1(now);Sz[x]+=Sz[now]; if(Son[x]<0 || Sz[now]>Sz[Son[x]]) Son[x]=now; } } void dfs2(int x,int tp){ id[x]=++Idex;hd[x]=tp; if(Son[x]<0) {el[x]=Idex;return;} dfs2(Son[x],tp); for(int point=head[x];point!=-1;point=then){ if(now!=Son[x]) dfs2(now,now); } el[x]=Idex; } void Push_down(int o,int l,int r){ if(s[o].pt!=-1){ int mid=(l+r)>>1; s[o<<1].pt=s[o].pt;s[o<<1].sm=(mid-l+1)*s[o].pt; s[o<<1|1].pt=s[o].pt;s[o<<1|1].sm=(r-mid)*s[o].pt; s[o].pt=-1; } } void Update(int o){ s[o].sm=s[o<<1].sm+s[o<<1|1].sm; } void Modify(int o,int l,int r,int al,int ar,int d){ Push_down(o,l,r); if(l==al && r==ar){s[o].pt=d,s[o].sm=(r-l+1)*d;return ;} int mid=(l+r)>>1; if(al>mid) Modify(o<<1|1,mid+1,r,al,ar,d); else if(ar<=mid) Modify(o<<1,l,mid,al,ar,d); else Modify(o<<1|1,mid+1,r,mid+1,ar,d),Modify(o<<1,l,mid,al,mid,d); Update(o); } int Ask(int o,int l,int r,int x){ Push_down(o,l,r); if(l==r) return s[o].sm; int mid=(l+r)>>1; if(x>mid) return Ask(o<<1|1,mid+1,r,x); return Ask(o<<1,l,mid,x); } void Add(int x){ int last; while(x){ Modify(1,1,n,id[hd[x]],id[x],1); x=fa[hd[x]]; } } void Del(int x){ Modify(1,1,n,id[x],el[x],0); } int main(){ #ifndef ONLINE_JUDGE freopen("manager.in","r",stdin); freopen("manager.out","w",stdout); #endif
 
    int x,Kase,sum1,sum2; char ord[10]; n=in(); for(int i=1;i<=n;i++) head[i]=-1; for(int i=2;i<=n;i++) x=in(),addedge(x+1,i),fa[i]=x+1; dfs1(1); dfs2(1,1); Kase=in(); while(Kase--){ scanf("%s%d",ord,&x);x++; if(ord[0]=='i'){ if(Ask(1,1,n,id[x])==1){puts("0");} else{ sum1=s[1].sm; Add(x); printf("%d\n",s[1].sm-sum1); } } else{ if(Ask(1,1,n,id[x])==0){puts("0");} else{ sum1=s[1].sm; Del(x); printf("%d\n",sum1-s[1].sm); } } } return 0; }
View Code


T3 壽司晚宴

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4197

有一些數字[2...n],你須要從中選出兩個子集A[a1..ak],B[b1..bl],使得這兩個子集中沒有任何一組i,j知足gcd(ai,bj)!=1,求方案數對p的模值

咱們能夠對每個數字考慮,看能不能放到集合A或者集合B。

若是要放到A,顯然它的全部因子都不能出如今B,反之亦然。

那麼咱們能夠用狀壓表示A集合已經有了哪些質因子以及B集合已經有了哪些質因子,而後用f[i][j]表示兩邊的質因子控制狀況,其中i&j==0

而後對於目前元素x,若是x的質因子表示bit(x)&j==0就能夠併到i中去,而後大概就是這樣子的遞推...

固然這樣是不行的,由於質因子個數太多了...可是咱們發現小於sqrt(500)的質因子只有8個...因而咱們就能夠狀壓表示這八個質因子的掌握狀況。

首先將全部的數拆分紅兩個部分,一個是>sqrt(500)的質因子kind,一個是剩下的質因子所構成的一個狀壓表示。

把全部數按kind分類,若是不存在>sqrt(500)的質因子那就兩邊均可以放,若是存在的話,對於同一個kind,只能有一個集合取,可是能夠取若干個。

那咱們就能夠設一個p[0][S1][S2],一個p[1][S1][S2]分別表示當前kind由A集合選和由B集合選的兩種方案下,同時A集合8個質因子掌握狀況爲S1,B集合8個質因子掌握狀況爲S2的方案數。

總的又是一個f[S1][S2]表示A集合和B集合對於8個質因子的掌握狀況。

當前加入一個新的數,若其沒有>sqrt(500)的質因子或者沒有他是一個新的kind分類的開端,就將p[0],p[1]先設置爲沒有加入這個元素前的狀態f[]。而後分別枚舉討論加入後的影響。

若其所在的kind是正在討論的部分,那麼就沿着以前留下的p[0],p[1]接着討論。由於若是說一個kind考慮完了,f[]須要將兩邊的元素整合起來,也就是不一樣kind的考慮是獨立的。因此在這個kind下兩邊的討論就能夠歸總到f[]中去。可是p[0]和p[1]都創建在沒有選這一類以前,因此要是要整合的話,須要減去在這一類kind以前的選擇方案數,不然就被加了兩遍。即f[]=p[0]+p[1]-f[];

感受思路仍是挺神奇的.想到分紅兩個部分的質因子而後用狀壓的思路值得積累.

#include<cstdio> #include<cstring> #include<algorithm>
 
using namespace std; const int maxn=510; const int Lim=(1<<8); const int maxl=(1<<8)+10; const int Small=19; int n,mod; int Prime[9]={0,2,3,5,7,11,13,17,19}; int f[maxl][maxl]; int p[2][maxl][maxl]; struct Node{ int kind,bit; }s[maxn]; bool cmp(const Node &A,const Node &B){ return A.kind<B.kind; } int main(){ #ifndef ONLINE_JUDGE freopen("dinner.in","r",stdin); freopen("dinner.out","w",stdout); #endif scanf("%d%d",&n,&mod); for(int i=2;i<=n;i++){ s[i].kind=i; for(int j=1;j<=8;j++){ if(i%Prime[j]==0){ s[i].bit|=(1<<j-1); while(s[i].kind%Prime[j]==0) s[i].kind/=Prime[j]; } } } sort(s+2,s+n+1,cmp); f[0][0]=1; for(int i=2;i<=n;i++){ if(s[i].kind!=s[i-1].kind || s[i].kind==1){ memcpy(p[0],f,sizeof(f)); memcpy(p[1],f,sizeof(f)); } for(int S1=Lim-1;S1>=0;S1--) for(int S2=Lim-1;S2>=0;S2--) if(!(S1&S2)){ if(!(s[i].bit&S2)) p[0][S1|s[i].bit][S2]=(p[0][S1|s[i].bit][S2]+p[0][S1][S2])%mod; if(!(s[i].bit&S1)) p[1][S1][S2|s[i].bit]=(p[1][S1][S2|s[i].bit]+p[1][S1][S2])%mod; } if(s[i].kind!=s[i+1].kind || s[i].kind==1){ for(int S1=0;S1<Lim;S1++) for(int S2=0;S2<Lim;S2++) if(!(S1&S2)) f[S1][S2]=((p[0][S1][S2]+p[1][S1][S2]-f[S1][S2])%mod+mod)%mod; } } int ans=0; for(int S1=0;S1<Lim;S1++) for(int S2=0;S2<Lim;S2++) if(!(S1&S2)) ans=(ans+f[S1][S2])%mod; printf("%d",ans); return 0; }
View Code

 

 

Day2

T1 荷馬史詩

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4198

讓你用一個k進製表示n個數,它們分別出現的次數爲a1...an,問怎樣設計這n個數既使得沒有一個是另外一個的前綴,又使得這個總的使用字母最短,知足前面兩個的基礎上,還使得最長的字母最短.

容易讓人想起哈夫曼編碼.而後這個就是k進制下的,那就是k叉哈夫曼樹了...

而後要加幾個0保證這棵樹長得比較好看呢?...

首先確定是和k-1有關對吧...由於每k個變成1個,其實就是消失了k-1個.

而後這樣理解了以後,就很好弄了,首先設A=n%(k-1)看一下剩幾個.由於最後要剩下一個,因此A=1正好,而後其它的就是湊成剩下一個了...

若是A>1,那就是[(n-1)-A]+1=n-A個.若是是A<1,那就是1-A個.

其實也不是沒有把上面三個歸納起來的方法: 須要+0的個數爲: (n-A)%(n-1)個.

模板題+1,良心送分.

#include<cstdio> #include<cstring> #include<queue> #include<algorithm>
 
using namespace std; typedef long long ll; struct Node{ ll dt; int lv; bool operator < (const Node &A) const{ if(A.dt!=dt) return A.dt<dt; return A.lv<lv; } }; int n,k; priority_queue<Node>q; int main(){ #ifndef ONLINE_JUDGE freopen("epic.in","r",stdin); freopen("epic.out","w",stdout); #endif ll x; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%lld",&x),q.push((Node){x,1}); x=n%(k-1); if(x==1) x=0; else if(x==0) x=1; else x=k-x; if(k==2) x=0; for(int i=1;i<=x;i++) q.push((Node){0,1}); ll ans=0; Node now; while(!q.empty()){ now=q.top();q.pop(); if(q.empty()){ printf("%lld\n%d",ans,now.lv-1); break; } for(int i=1;i<k;i++){ now.dt+=q.top().dt; now.lv=max(now.lv,q.top().lv); q.pop(); } now.lv++; ans+=now.dt; q.push(now); } return 0; }
View Code

 

T2 品酒大會

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4199

給一個串,設定兩個位置i,j爲r類似是說,從它們倆開始記爲1,日後走r個構成的串都是相同的.每一個位置有權值,一個點對的值=它們兩個位置的權值之積.

有兩問,第一問問你全部的r類似分別有多少個.第二問問你r類似的點對中最大的權值是多少.

而後就聯想到了差別這道題...感受十分的相似.由lca的選擇也就能夠統計到mx[lca]類似當中去.

這題的計數和那題也是同樣的.那麼求最大值和最小值聯想一下也是能夠想出來的.都是樹形dp的一部分吧.

可是注意每次計數就只要考慮跨過LCA的貢獻,而後就能獲得最長爲mx[lca]類似的,

最後再累加一下就能獲得總共的了.

原題的積累仍是比較重要的.

#include<cstdio> #include<cstring> #include<algorithm>

using namespace std; inline int in(){ int x=0,f=1;char ch=getchar(); while((ch>'9' || ch<'0') && ch!='-') ch=getchar(); if(ch=='-') ch=getchar(),f=-1; while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f; } const int maxn=600010; const int INF=0x3f3f3f3f; typedef long long ll; int n,last,cnt; int v[maxn]; int a[maxn][26],mx[maxn],fa[maxn]; int rz[maxn],bg[maxn],sm[maxn],mk[maxn]; int T[maxn],Seq[maxn]; int mk_sm[maxn],mk_bg[maxn]; ll rec1[maxn],rec2[maxn]; char ch[maxn]; void extend(int c,int x){ int p=last,np=last=++cnt; mx[np]=mx[p]+1,rz[np]=mk[np]=1,bg[np]=sm[np]=mk_sm[np]=mk_bg[np]=v[x]; while(!a[p][c] && p) a[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else{ int q=a[p][c]; if(mx[q]==mx[p]+1) fa[np]=q; else{ int nq=++cnt; mx[nq]=mx[p]+1; bg[nq]=mk_bg[nq]=-INF; sm[nq]=mk_sm[nq]=INF; memcpy(a[nq],a[q],sizeof(a[q])); fa[nq]=fa[q]; fa[np]=fa[q]=nq; while(a[p][c]==q) a[p][c]=nq,p=fa[p]; } } } void get_order(){ for(int i=1;i<=cnt;i++) T[mx[i]]++; for(int i=2;i<=n;i++) T[i]+=T[i-1]; for(int i=1;i<=cnt;i++) Seq[T[mx[i]]--]=i; } void Dp(){ int x; for(int i=cnt;i>=1;i--){ x=Seq[i]; rz[fa[x]]+=rz[x]; bg[fa[x]]=max(bg[fa[x]],bg[x]); sm[fa[x]]=min(sm[fa[x]],sm[x]); } for(int i=1;i<=n;i++) rec2[i]=-0x3f3f3f3f3f3f3f3f; for(int i=cnt;i>=1;i--){ x=Seq[i]; rec1[mx[fa[x]]]+=(ll)rz[x]*mk[fa[x]]; mk[fa[x]]+=rz[x]; if(mk_bg[fa[x]]!=-INF) rec2[mx[fa[x]]]=max(rec2[mx[fa[x]]],(ll)mk_bg[fa[x]]*bg[x]); if(mk_sm[fa[x]]!=INF) rec2[mx[fa[x]]]=max(rec2[mx[fa[x]]],(ll)mk_sm[fa[x]]*sm[x]); mk_bg[fa[x]]=max(mk_bg[fa[x]],bg[x]); mk_sm[fa[x]]=min(mk_sm[fa[x]],sm[x]); } int max1=-INF,max2=-INF,min1=INF,min2=INF; ll ans1=(ll)n*(n-1)/2,ans2; for(int i=1;i<=n;i++){ if(v[i]>=max1) max2=max1,max1=v[i]; else if(v[i]>max2) max2=v[i]; if(v[i]<=min1) min2=min1,min1=v[i]; else if(v[i]<min2) min2=v[i]; } if(max2==-INF && min2!=INF) ans2=(ll)min1*min2; else if(max2!=-INF && min2==INF) ans2=(ll)max1*max2; else ans2=max((ll)min1*min2,(ll)max1*max2); printf("%lld %lld\n",ans1,ans2); for(int i=n-1;i>=1;i--) rec1[i]+=rec1[i+1],rec2[i]=max(rec2[i],rec2[i+1]); for(int i=1;i<n;i++){ if(!rec1[i]){printf("0 0\n");} else printf("%lld %lld\n",rec1[i],rec2[i]); } } int main(){ #ifndef ONLINE_JUDGE freopen("savour.in","r",stdin); freopen("savour.out","w",stdout); #endif last=cnt=1; n=in(); scanf("%s",ch); for(int i=0;i<n;i++) v[i]=in(); for(int i=n-1;i>=0;i--) extend(ch[i]-'a',i); get_order(); Dp(); return 0; }
View Code

 

T3 小園丁與老司機

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4200

空白讓人思考。

相關文章
相關標籤/搜索