度过了神奇的学考时间,又回到了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; }
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; }
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; }
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; }
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; }
T3 小园丁与老司机
AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4200
空白让人思考。