NOI2015 题解
2015-08-10 18:45
330 查看
D1T1 程序自动分析
题目大意:给定10910^9个变量和nn个等于/不等于的关系,判断能否存在一组解满足所有关系
并查集傻逼题,NOIP小孩都会做
离散化一下,然后把相等的都用并查集并起来,判断每对不等关系是不是在同一并查集中就行了
代码没拷回来,懒得再写一遍了
D2T2 软件包管理器
题目大意:给定一棵有根树,每个点有黑白两种颜色,初始都为白色,每次进行以下两种操作:
1.将某个点所在的子树染白
2.将某个点到根路径上的点染黑
求每次操作后有多少点的颜色发生了改变
树剖傻逼题,NOIP小孩都会做
维护一棵有根树,支持链查询,链修改,子树查询,子树修改
显然链剖就行了
代码也没拷回来,懒得再写一遍了
D3T3 寿司晚宴
题目大意:给定2...n2...n一共n−1n-1个数字,第一个人选择一些数字,第二个人选择一些数字,要求第一个人选的任意一个数字和第二个人选择的任意一个数字都互质,求方案数
n≤500n\leq500
DP傻逼题,我竟然不会做
首先我们把一个数字看成这样一坨东西:
小于500−−−√\sqrt{500}的质数只有88个,我们开一个282^8的数组记录每个质数是否出现
大于500−−−√\sqrt{500}的质数只会在这个数字中出现最多一个(即一个数字中最多只有一个大于500−−−√\sqrt{500}的质因数),我们把数字按照这个质数分类,没有大于500−−−√\sqrt{500}的质因数的数每个自成一类
那么显然一类数最多只有一个人能取 两个人都取同一类数的话就会出现不互质的情况
然后我们就可以DP辣!!
令fi,jf_{i,j}表示第一个人选择的质数集合为ii,第二个人选择的质数集合为jj的方案数,其中i,ji,j的取值范围为[0,28)[0,2^8),二进制的第ii位为11表示第ii个质数已经取到了
令gi,j,kg_{i,j,k}表示第i+1i+1个人选择了这一类的数(可以不选)后第一个人选择的质数集合为jj,第二个人选择的质数集合为kk的方案数
枚举每一类数,首先将ff数组复制一份给g0g_0和g1g_1,然后这一类数中的每个数字来更新g0g_0和g1g_1,最后用g0+g1−fg_0+g_1-f替换ff数组即可
什么你问我为何要减掉ff?很简单因为这一类数两个人都不选的状态在g0g_0和g1g_1中都出现了啊~
时间复杂度O(500∗216)O(500*2^{16})
代码:
D2T1 荷马史诗
题目大意:给定一篇文章中每个字母的出现次数,要求为每一个字母设计一个kk进制编码使得不存在一个字母是另一个字母的前缀且总长度最小,在此基础上要求最长的字母最短
k=2k=2时第一问就是裸的哈夫曼树,不知道的可以想象合并果子
k-哈夫曼树就是一次合并k个,如果有零头的话就先把零头合并
最长的字母最短就是每次找高度最小的树合并
别问我怎么证明,我连合并果子都不会证…
代码:
D2T2 品酒大会
题目大意:给定一个长度为nn字符串,每个后缀有一个权值ff,对于l=0...n−1l=0...n-1输出Lcp(i,j)≥l(i<j)Lcp(i,j)\geq l(i的后缀对(i,j)(i,j)的个数以及最大的fi∗fjf_i*f_j
神木韵律的超n^n级弱化版= =?
求出后缀数组后从大到小枚举heightheight值,然后对于每个heightheight值,两端的集合中任意一对后缀的LCP都是这个heightheight,我们统计答案之后合并两端的集合,用并查集维护即可
代码:
D2T3 小园丁与老司机
题目大意:给定平面上nn个点,从原点出发,每次可以走到左、左上、上、右上、右的每个方向上第一个能到的点,一个点走过即消失,求:
1.最多能到达多少个点
2.输出任意一组方案
3.将所有方案中的左上、上、右上的路径连起来,求最小路径覆盖
DP,记录方案,建图跑最小流,这题只有细节,只有细节……
我的代码写了个费用流,被第7个点卡出翔过不了……
题目大意:给定10910^9个变量和nn个等于/不等于的关系,判断能否存在一组解满足所有关系
并查集傻逼题,NOIP小孩都会做
离散化一下,然后把相等的都用并查集并起来,判断每对不等关系是不是在同一并查集中就行了
代码没拷回来,懒得再写一遍了
D2T2 软件包管理器
题目大意:给定一棵有根树,每个点有黑白两种颜色,初始都为白色,每次进行以下两种操作:
1.将某个点所在的子树染白
2.将某个点到根路径上的点染黑
求每次操作后有多少点的颜色发生了改变
树剖傻逼题,NOIP小孩都会做
维护一棵有根树,支持链查询,链修改,子树查询,子树修改
显然链剖就行了
代码也没拷回来,懒得再写一遍了
D3T3 寿司晚宴
题目大意:给定2...n2...n一共n−1n-1个数字,第一个人选择一些数字,第二个人选择一些数字,要求第一个人选的任意一个数字和第二个人选择的任意一个数字都互质,求方案数
n≤500n\leq500
DP傻逼题,我竟然不会做
首先我们把一个数字看成这样一坨东西:
小于500−−−√\sqrt{500}的质数只有88个,我们开一个282^8的数组记录每个质数是否出现
大于500−−−√\sqrt{500}的质数只会在这个数字中出现最多一个(即一个数字中最多只有一个大于500−−−√\sqrt{500}的质因数),我们把数字按照这个质数分类,没有大于500−−−√\sqrt{500}的质因数的数每个自成一类
那么显然一类数最多只有一个人能取 两个人都取同一类数的话就会出现不互质的情况
然后我们就可以DP辣!!
令fi,jf_{i,j}表示第一个人选择的质数集合为ii,第二个人选择的质数集合为jj的方案数,其中i,ji,j的取值范围为[0,28)[0,2^8),二进制的第ii位为11表示第ii个质数已经取到了
令gi,j,kg_{i,j,k}表示第i+1i+1个人选择了这一类的数(可以不选)后第一个人选择的质数集合为jj,第二个人选择的质数集合为kk的方案数
枚举每一类数,首先将ff数组复制一份给g0g_0和g1g_1,然后这一类数中的每个数字来更新g0g_0和g1g_1,最后用g0+g1−fg_0+g_1-f替换ff数组即可
什么你问我为何要减掉ff?很简单因为这一类数两个人都不选的状态在g0g_0和g1g_1中都出现了啊~
时间复杂度O(500∗216)O(500*2^{16})
代码:
[code]#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 550 using namespace std; const int prime[]={2,3,5,7,11,13,17,19}; int n,p; int f[1<<8][1<<8],g[2][1<<8][1<<8]; pair<int,int> stack[550]; int main() { int i,j,k; cin>>n>>p; for(i=1;i<=n;i++) { int temp=i; for(j=0;j<8;j++) if(temp%prime[j]==0) { stack[i].second|=1<<j; while(temp%prime[j]==0) temp/=prime[j]; } stack[i].first=temp; } sort(stack+2,stack+n+1); f[0][0]=1; for(i=2;i<=n;i++) { if( i==2 || stack[i].first==1 || stack[i].first!=stack[i-1].first ) { memcpy(g[0],f,sizeof f); memcpy(g[1],f,sizeof f); } for(j=255;~j;j--) for(k=255;~k;k--) { if( (stack[i].second&k)==0 ) (g[0][j|stack[i].second][k]+=g[0][j][k])%=p; if( (stack[i].second&j)==0 ) (g[1][j][k|stack[i].second]+=g[1][j][k])%=p; } if( i==n || stack[i].first==1 || stack[i].first!=stack[i+1].first ) { for(j=0;j<1<<8;j++) for(k=0;k<1<<8;k++) f[j][k]=((g[0][j][k]+g[1][j][k]-f[j][k])%p+p)%p; } } int ans=0; for(j=0;j<1<<8;j++) for(k=0;k<1<<8;k++) if( (j&k)==0 ) (ans+=f[j][k])%=p; cout<<ans<<endl; return 0; }
D2T1 荷马史诗
题目大意:给定一篇文章中每个字母的出现次数,要求为每一个字母设计一个kk进制编码使得不存在一个字母是另一个字母的前缀且总长度最小,在此基础上要求最长的字母最短
k=2k=2时第一问就是裸的哈夫曼树,不知道的可以想象合并果子
k-哈夫曼树就是一次合并k个,如果有零头的话就先把零头合并
最长的字母最短就是每次找高度最小的树合并
别问我怎么证明,我连合并果子都不会证…
代码:
[code]#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; struct abcd{ long long size; int dpt; abcd() {} abcd(long long _,int __): size(_),dpt(__) {} friend bool operator < (const abcd &x,const abcd &y) { if(x.size!=y.size) return x.size>y.size; return x.dpt>y.dpt; } }; int n,k,ans2; long long ans1; priority_queue<abcd> heap; void Merge(int cnt) { long long _size=0; int _dpt=0; while(cnt--) { abcd temp=heap.top();heap.pop(); _size+=temp.size; _dpt=max(_dpt,temp.dpt); } ans1+=_size; ans2=max(ans2,++_dpt); heap.push(abcd(_size,_dpt)); } int main() { int i;long long x; cin>>n>>k; for(i=1;i<=n;i++) { scanf("%lld",&x); heap.push(abcd(x,0)); } if( (n-1)%(k-1)+1!=1 && n!=1 ) Merge( min( (n-1)%(k-1)+1 , n ) ); while(heap.size()>1) Merge( min( k , (int)heap.size() ) ); cout<<ans1<<'\n'<<ans2<<endl; return 0; }
D2T2 品酒大会
题目大意:给定一个长度为nn字符串,每个后缀有一个权值ff,对于l=0...n−1l=0...n-1输出Lcp(i,j)≥l(i<j)Lcp(i,j)\geq l(i的后缀对(i,j)(i,j)的个数以及最大的fi∗fjf_i*f_j
神木韵律的超n^n级弱化版= =?
求出后缀数组后从大到小枚举heightheight值,然后对于每个heightheight值,两端的集合中任意一对后缀的LCP都是这个heightheight,我们统计答案之后合并两端的集合,用并查集维护即可
代码:
[code]#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 300300 using namespace std; int n; char s[M]; long long ans1[M],ans2[M]; namespace Suffix_Array{ int sa[M],rank[M],height[M]; int X[M],Y[M],temp[M],tot; void Get_Rank() { static int sum[M]; int i; for(i=1;i<=n;i++) sum[s[i]]++; for(i=1;i<=200;i++) sum[i]+=sum[i-1]; for(i=n;i;i--) sa[sum[s[i]]--]=i; for(i=1;i<=n;i++) { if( i==1 || s[sa[i-1]]!=s[sa[i]] ) ++tot; rank[sa[i]]=tot; } } void Radix_Sort(int key[],int order[]) { static int sum[M]; int i; for(i=0;i<=n;i++) sum[i]=0; for(i=1;i<=n;i++) sum[key[i]]++; for(i=1;i<=n;i++) sum[i]+=sum[i-1]; for(i=n;i;i--) temp[sum[key[order[i]]]--]=order[i]; for(i=1;i<=n;i++) order[i]=temp[i]; } void Get_Height() { int i,j,k; for(i=1;i<=n;i++) { if(rank[i]==1) continue; j=sa[rank[i]-1]; k=max(0,height[rank[i-1]]-1); while(s[i+k]==s[j+k]) ++k; height[rank[i]]=k; } } void Prefix_Doubling() { int i,j; Get_Rank(); for(j=1;j<=n;j<<=1) { for(i=1;i<=n;i++) { X[i]=rank[i]; Y[i]=i+j>n?0:rank[i+j]; sa[i]=i; } Radix_Sort(Y,sa); Radix_Sort(X,sa); for(tot=0,i=1;i<=n;i++) { if( i==1 || X[sa[i]]!=X[sa[i-1]] || Y[sa[i]]!=Y[sa[i-1]] ) ++tot; rank[sa[i]]=tot; } } Get_Height(); } } namespace Union_Find_Set{ int fa[M],rank[M],size[M],max_val[M],min_val[M]; int Find(int x) { if(!fa[x]) size[x]=1,fa[x]=x; if(fa[x]==x) return x; return fa[x]=Find(fa[x]); } void Union(int x,int y) { x=Find(x);y=Find(y); if(x==y) return ; if(rank[x]>rank[y]) swap(x,y); if(rank[x]==rank[y]) ++rank[y]; fa[x]=y;size[y]+=size[x]; max_val[y]=max(max_val[x],max_val[y]); min_val[y]=min(min_val[x],min_val[y]); } } bool Compare(int x,int y) { using namespace Suffix_Array; return height[x]>height[y]; } int main() { using namespace Suffix_Array; using namespace Union_Find_Set; int i,x,y; cin>>n;scanf("%s",s+1); Prefix_Doubling(); for(i=1;i<=n;i++) { scanf("%d",&x); max_val[Suffix_Array::rank[i]]=min_val[Suffix_Array::rank[i]]=x; } static int a[M]; for(i=2;i<=n;i++) a[i-1]=i; sort(a+1,a+n,Compare); memset(ans2,0xef,sizeof ans2); for(i=1;i<n;i++) { x=Find(a[i]-1),y=Find(a[i]); ans1[height[a[i]]]+=(long long)size[x]*size[y]; ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)min_val[x]*min_val[y]); ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)min_val[x]*max_val[y]); ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)max_val[x]*min_val[y]); ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)max_val[x]*max_val[y]); Union(x,y); } for(i=n-1;~i;i--) { ans1[i]+=ans1[i+1]; ans2[i]=max(ans2[i],ans2[i+1]); } for(i=0;i<n;i++) printf("%lld %lld\n",ans1[i],ans1[i]==0?0:ans2[i]); return 0; }
D2T3 小园丁与老司机
题目大意:给定平面上nn个点,从原点出发,每次可以走到左、左上、上、右上、右的每个方向上第一个能到的点,一个点走过即消失,求:
1.最多能到达多少个点
2.输出任意一组方案
3.将所有方案中的左上、上、右上的路径连起来,求最小路径覆盖
DP,记录方案,建图跑最小流,这题只有细节,只有细节……
我的代码写了个费用流,被第7个点卡出翔过不了……
[code]#include <map> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 50500 #define S (M-1) #define T (M-2) #define INF 0x3f3f3f3f using namespace std; struct Point{ int x,y,id; friend istream& operator >> (istream &_,Point &p) { return scanf("%d%d",&p.x,&p.y),_; } friend bool operator < (const Point &p1,const Point &p2) { if( p1.y!=p2.y ) return p1.y < p2.y ; return p1.x < p2.x ; } }points[M]; int n,ans1,ans1_ed,ans2; int f[M],g[M],from[M],_from[M]; map<int,int> m1,m2,m3; namespace Max_Cost_Max_Flow{ struct abcd{ int to,flow,cost,next; }table[2002002]; int head[M],tot=1; void Add(int x,int y,int f,int c) { table[++tot].to=y; table[tot].flow=f; table[tot].cost=c; table[tot].next=head[x]; head[x]=tot; } void Link(int x,int y,int f,int c) { Add(x,y,f,c); Add(y,x,0,-c); } bool Edmonds_Karp() { static int q[65540],flow[M],cost[M],from[M]; static unsigned short r,h; static bool v[M]; int i; memset(cost,0xef,sizeof cost); flow[S]=INF;cost[S]=0;q[++r]=S; while(r!=h) { int x=q[++h];v[x]=false; for(i=head[x];i;i=table[i].next) if(table[i].flow&&cost[table[i].to]<cost[x]+table[i].cost) { cost[table[i].to]=cost[x]+table[i].cost; flow[table[i].to]=min(flow[x],table[i].flow); from[table[i].to]=i; if(!v[table[i].to]) v[table[i].to]=true,q[++r]=table[i].to; } } if(cost[T]<=0) return false; ans2+=flow[T]; for(i=from[T];i;i=from[table[i^1].to]) table[i].flow-=flow[T],table[i^1].flow+=flow[T]; return true; } } int main() { using namespace Max_Cost_Max_Flow; int i,j; cin>>n; for(i=1;i<=n;i++) cin>>points[i],points[i].id=i; sort(points+1,points+n+1); static int stack[M],top; memset(f,0xef,sizeof f);f[0]=0; for(i=0;i<=n;i++) { if(m1.find(points[i].x)!=m1.end()) { int temp=m1[points[i].x]; if(f[temp]+1>f[i]) f[i]=f[temp]+1,from[i]=temp; } m1[points[i].x]=i; if(m2.find(points[i].x+points[i].y)!=m2.end()) { int temp=m2[points[i].x+points[i].y]; if(f[temp]+1>f[i]) f[i]=f[temp]+1,from[i]=temp; } m2[points[i].x+points[i].y]=i; if(m3.find(points[i].x-points[i].y)!=m3.end()) { int temp=m3[points[i].x-points[i].y]; if(f[temp]+1>f[i]) f[i]=f[temp]+1,from[i]=temp; } m3[points[i].x-points[i].y]=i; stack[++top]=i; if( i==n || points[i+1].y!=points[i].y ) { static int _f[M]; for(j=1;j<=top;j++) { _f[stack[j]]=f[stack[j]]; _from[stack[j]]=from[stack[j]]; } int max_pos=-1; for(j=2;j<=top;j++) { if( max_pos==-1 || _f[stack[j-1]]>_f[stack[max_pos]] ) max_pos=j-1; if( _f[stack[max_pos]]+(j-1)>f[stack[j]] ) f[stack[j]]=_f[stack[max_pos]]+(j-1),from[stack[j]]=stack[max_pos]; } max_pos=-1; for(j=top-1;j;j--) { if( max_pos==-1 || _f[stack[j+1]]>_f[stack[max_pos]] ) max_pos=j+1; if( _f[stack[max_pos]]+(top-j)>f[stack[j]] ) f[stack[j]]=_f[stack[max_pos]]+(top-j),from[stack[j]]=stack[max_pos]; } for(j=1;j<=top;j++) if(f[stack[j]]>ans1) ans1=f[stack[j]],ans1_ed=stack[j]; top=0; } } cout<<ans1<<endl; bool flag=false; while(ans1_ed) { stack[++top]=points[ans1_ed].id; if(flag) { flag=false; ans1_ed=_from[ans1_ed]; continue; } if(points[ans1_ed].y==points[from[ans1_ed]].y) { if(ans1_ed>from[ans1_ed]) { for(i=ans1_ed-1;i!=from[ans1_ed];i--) stack[++top]=points[i].id; for(i--;points[i].y==points[ans1_ed].y;i--); for(i++;i!=from[ans1_ed];i++) stack[++top]=points[i].id; } else { for(i=ans1_ed+1;i!=from[ans1_ed];i++) stack[++top]=points[i].id; for(i++;points[i].y==points[ans1_ed].y;i++); for(i--;i!=from[ans1_ed];i--) stack[++top]=points[i].id; } flag=true; } ans1_ed=from[ans1_ed]; } //cout<<top<<endl; while(top) printf("%d ",stack[top--]); puts(""); m1.clear(); m2.clear(); m3.clear(); memcpy(g,f,sizeof g); for(i=n;~i;i--) { f[i]=(i!=0); if(m1.find(points[i].x)!=m1.end()) { int temp=m1[points[i].x]; f[i]=max(f[temp]+(i!=0),f[i]); //printf("%d\n",g[i]+f[temp]); if(g[i]+f[temp]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m1[points[i].x]=i; if(m2.find(points[i].x+points[i].y)!=m2.end()) { int temp=m2[points[i].x+points[i].y]; f[i]=max(f[temp]+(i!=0),f[i]); //printf("%d\n",g[i]+f[temp]); if(g[i]+f[temp]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m2[points[i].x+points[i].y]=i; if(m3.find(points[i].x-points[i].y)!=m3.end()) { int temp=m3[points[i].x-points[i].y]; f[i]=max(f[temp]+(i!=0),f[i]); //printf("%d\n",g[i]+f[temp]); if(g[i]+f[temp]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m3[points[i].x-points[i].y]=i; stack[++top]=i; if( i==0 || points[i-1].y!=points[i].y ) { static int _f[M]; for(j=1;j<=top;j++) _f[stack[j]]=f[stack[j]]; int max_pos=-1; for(j=2;j<=top;j++) { if( max_pos==-1 || _f[stack[j-1]]+(top-j+1)>_f[stack[max_pos]]+(top-max_pos) ) max_pos=j-1; if( _f[stack[max_pos]]+(top-max_pos)>f[stack[j]] ) f[stack[j]]=_f[stack[max_pos]]+(top-max_pos); } max_pos=-1; for(j=top-1;j;j--) { if( max_pos==-1 || _f[stack[j+1]]+(j)>_f[stack[max_pos]]+(max_pos-1) ) max_pos=j+1; if( _f[stack[max_pos]]+(max_pos-1)>f[stack[j]] ) f[stack[j]]=_f[stack[max_pos]]+(max_pos-1); } top=0; } } m1.clear(); m2.clear(); m3.clear(); /* for(i=0;i<=n;i++) printf("%d\n",f[i]+g[i]); for(i=0;i<=n;i++) { if(m1.find(points[i].x)!=m1.end()) { int temp=m1[points[i].x]; if(g[temp]+f[i]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m1[points[i].x]=i; if(m2.find(points[i].x+points[i].y)!=m2.end()) { int temp=m2[points[i].x+points[i].y]; if(g[temp]+f[i]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m2[points[i].x+points[i].y]=i; if(m3.find(points[i].x-points[i].y)!=m3.end()) { int temp=m3[points[i].x-points[i].y]; if(g[temp]+f[i]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m3[points[i].x-points[i].y]=i; } */ for(i=0;i<=n;i++) { Link(S,i,INF,0); Link(i,T,INF,0); } while( Edmonds_Karp() ); cout<<ans2<<endl; return 0; }
相关文章推荐
- SAPUI5是什么?Fiori告诉我们将来UI开发用哪种技术?
- 直接拿来用!十大Material Design开源项目
- Linux指令
- 属性动画学习总结
- java 权限管理框架
- C# 读取oracle 中文乱码的解决方案
- windows用一键安装包安装(推荐)
- BestCoder Round #50 Run 即 hdu5365(暴力枚举)
- CentOS/Linux连接iscsi磁盘阵列
- Linux命令学习:date
- C#之Ref,Out以及TryParse()的用法
- MongoDB CUD文档
- (4.2.20)静态对话框类CustomProgressDialog
- IOS 编码规范建议
- POJ 2421--Constructing Roads【水题 && 最小生成树 && kruskal】
- APP图标搜集网址大全
- Cocos2d-x 3.2开发《大富翁》游戏配音
- struts2-dojo-plugin-2.3.1.2.jar!/struts-plugin.xml:29:119
- 无责任Windows Azure SDK .NET开发入门篇二[使用Azure AD 进行身份验证--2.1使用Azure AD需要了解几个概念]
- struts2-dojo-plugin-2.3.1.2.jar!/struts-plugin.xml:29:119