这几天一直没怎么更新博客,是因为白天一直考试,晚上一直做往年的noip,自然没空处理错题……今天晚上终于腾出手来了,那么先从好做点的2014年开始整理,蒟蒻的呕心沥血之作系列,开始更新!
2014年总体来说挺简单的,就是两个t3有点麻烦,表示d1蒟蒻都能水到260
t1:生活大爆炸版石头剪刀布
这个题纯模拟啊,先预处理下胜负关系,然后for一遍,取下当前状态进行判断就好
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,na,nb,ans1,ans2,a[210],b[210],d[5][5],ta=1,tb=1; int main() { scanf("%d%d%d",&n,&na,&nb); for(int i=1;i<=na;i++) scanf("%d",&a[i]); for(int i=1;i<=nb;i++) scanf("%d",&b[i]); d[0][1]=-1,d[0][2]=1,d[0][3]=1,d[0][4]=-1;//d[i][j]表示a出i,b出j的时候的输赢情况 d[1][0]=1,d[1][2]=-1,d[1][3]=1,d[1][4]=-1; d[2][0]=-1,d[2][1]=1,d[2][3]=-1,d[2][4]=1; d[3][0]=-1,d[3][1]=-1,d[3][2]=1,d[3][4]=1; d[4][0]=1,d[4][1]=1,d[4][2]=-1,d[4][3]=-1; for(int i=1;i<=n;i++) { ta=(ta-1)%na+1,tb=(tb-1)%nb+1;//直接枚举现在的状态,(x-1)/y+1是一个小技巧,这个式子的范围是1~y,就不会出现取模时候的0~y-1了 if(d[a[ta]][b[tb]]==1) ans1++; else if(d[a[ta]][b[tb]]==-1) ans2++; ta++,tb++; } printf("%d %d",ans1,ans2); }
t2:联合权值
这个题是棵树,联合权值就有两种情况,一是爷爷和孙子,二是兄弟之间(写的我好虚),那么联合权值其实dfs一遍就好
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; const long long mo=10007; long long n,x,y,tail,s[200020],w[200020],ji[200020],ans1,ans2,head[200020],mx[200020]; struct in { long long to,ne; }ter[400040]; inline void build(int f,int l) { ter[++tail]=(in){l,head[f]},head[f]=tail; ter[++tail]=(in){f,head[l]},head[l]=tail; } void dfs(int x,int f) { bool flag=0; for(int i=head[x];i>0;i=ter[i].ne)//求祖孙之间的关系 { int t=ter[i].to; if(t==f) continue; flag=1; dfs(t,x); s[x]+=w[t];//统计自己蛾子的w值之和 //cout<<s[x]<<' '<<w[t]<<endl; if(mx[x]<w[t])//求个最大值 mx[x]=w[t],ji[x]=t;//记录下哪个点是最大值 ans1+=((w[x]%mo)*(s[t]%mo))%mo;//权值和等于它蛾子的s乘上它本身的w值的和 ans2=max(ans2,w[x]*mx[t]);//取联合权值的最大值 } if(!flag) return; long long tot=s[x],m2=0; //cout<<tot<<' '<<x<<endl; for(int i=head[x];i>0;i=ter[i].ne)// 求兄弟之间的关系 { int t=ter[i].to; if(t==f)//不能到它的父节点上 continue; if(w[t]>m2&&ji[x]!=t)//更新次小值 m2=w[t]; tot-=w[t]; ans1+=((w[t]%mo)*(tot%mo))%mo;//这个点与它的所有的兄弟的联合权值之和 } ans2=max(ans2,mx[x]*m2);/**/ } /* 这个题看似复杂,其实很简单。首先我们可以很容易的知道,这个图是一棵树 转化成树之后,只有两种情况会产生联合权值了,一是一个点与它的兄弟,二是一个点和它的孙子 所以我们可以用dfs,求每个点自己蛾子当中的w最大值mx和自己蛾子的权值和s 在具体求的时候,我们分情况讨论 一是祖孙的情况,求的时候用自己蛾子的mx和s(很好理解) 二是兄弟的情况,求一下非严格的次大值,开始前将变量tot赋值为s,每到一个蛾子时候令tot减去蛾子的w,用tot与蛾子的w相乘(为了去重) 最后记得*2(题目要求正着反着都可以) */ int main() { //freopen("link.in","r",stdin); //freopen("link.out","w",stdout); scanf("%lld",&n); memset(head,-1,sizeof(head)); for(int i=1;i<=n-1;i++) scanf("%lld%lld",&x,&y),build(x,y);//建树 for(int i=1;i<=n;i++) scanf("%lld",&w[i]); dfs(1,0);//处理 cout<<ans2<<' '<<(ans1*2%mo);//注意最大值不能取模,因为题目只要求权值之和取模 fclose(stdin); fclose(stdout); return 0; }
t3:飞扬的小鸟
这个题就是二维dp,dp[i][j]表示第i列第j行要想到达这至少需要点击几次题目,再加上个刷表法(就是一口气把一个点所有能关系到的状态都推出去),然后就可以了,之前做的时候打次了……
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m,k,a,b,c,dp[10010][1010],sh[10010],xi[10010],ans=2139062143,tail; struct in { int x,y; }ter[10010]; bool flag[10010]; inline void re(int &a) { a=0; char b=getchar(); while(b<'0'||b>'9') b=getchar(); while(b>='0'&&b<='9') a=a*10+b-'0',b=getchar(); } int main() { re(n),re(m),re(k); memset(dp,127,sizeof(dp)); for(int i=0;i<=n-1;i++) re(sh[i]),re(xi[i]); for(int i=0;i<=n;i++) ter[i]=(in){1,m}; for(int i=1;i<=k;i++) re(a),re(b),re(c),ter[a]=(in){b+1,c-1},flag[a]=1;//不能擦到障碍物上 for(int i=0;i<=m;i++)//开始一下都不用跳 dp[0][i]=0; for(int i=0;i<=n-1;i++) for(int j=ter[i].y;j>=ter[i].x;j--) { if(dp[i][j]>=2139062143)//如果这个地方无法转移直接跳过 continue; tail=i;//记录下最远能够到哪里 dp[i+1][j-xi[i]]=min(dp[i+1][j-xi[i]],dp[i][j]);//先处理下落的情况 for(int k=1;k<=(m-j)/sh[i]+1;k++)//这个地方运用了刷表法,个人认为就是一次性将状态推出去,因为就算是普通的dp,也是不断比较,如此我们可以直接往前推,每次推出去都比较一下,就可以成功转移了 { if(dp[i+1][min(k*sh[i]+j,m)]>dp[i][j]+k) dp[i+1][min(k*sh[i]+j,m)]=dp[i][j]+k; else//如果现在就不能更新,那么上面的也无法更新,因为上面之前的状态也是从下面转移而来,下面都不能更新了,上面也无从说起 break; } } for(int i=1;i<=m;i++)//取个最小值,因为只要到达n就好 ans=min(ans,dp[n][i]); if(ans<2139062143)//如果最小值有小于初始值的情况,说明能够到达答案 cout<<1<<'\n'<<ans; else//否则不能 { ans=0; for(int i=0;i<=tail;i++) { if(flag[i]) ans++; } cout<<0<<'\n'<<ans; } }
d1完成,下面是d2!