2015 NOIP提高组 复赛解题报告 C++
2017-10-27 14:50
417 查看
原文地址:http://blog.csdn.net/clove_unique
目录(?)[+]
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,i,j,num;
int a[50][50];
int main(){
scanf("%d",&n);
m=(n/2)+1;
a[1][m]=1;
num=1;
i=1; j=m;
while (num<n*n){
if (i==1&&j!=n) {a
[j+1]=++num; i=n; ++j;}
else if (i!=1&&j==n) {a[i-1][1]=++num; --i; j=1;}
else if (i==1&&j==n) {a[i+1][j]=++num; ++i;}
else if (i!=1&&j!=n){
if (!a[i-1][j+1]) {a[i-1][j+1]=++num; --i; ++j;}
else {a[i+1][j]=++num; ++i;}
}
}
for (int i=1;i<=n;++i){
for (int j=1;j<n;++j)
printf("%d ",a[i][j]);
printf("%d\n",a[i]
);
}
return 0;
}
T2 读了一遍题就看出来是最小环。刚开始忽略了有好几个联通块,大数据跑不对。最后写了个并查集+dfs,AC。水。
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 2100000000
#define N 200005
using namespace std;
int n,ans,y;
int f
,next
,h
;
bool b
;
inline int in(){
int x=0; char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
int find(int x){
if (x==f[x]) return x;
f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y){
int f1=find(x);
int f2=find(y);
f[f1]=f2;
}
void dfs(int x,int dep){
b[x]=true; h[x]=dep;
if (!b[next[x]])
dfs(next[x],dep+1);
else{
int k=h[x]-h[next[x]]+1;
ans=min(ans,k);
}
}
int main(){
n=in(); ans=inf;
for (int i=1;i<=n;++i)
f[i]=i;
for (int i=1;i<=n;++i){
y=in();
next[i]=y;
if (find(i)!=find(y))
merge(i,y);
else{
memset(b,0,sizeof(b));
dfs(i,1);
}
}
printf("%d",ans);
return 0;
}
T3 时限2s内存1G就知道这道题一定很神。刚开始看到了之后一点思路都没有,以为是dp但是发现状态太多压不下。而且还有多组数据一旦跑不对就挂了。30分打表。
后来听到TA大爷说就是个dfs,把每一种情况编上号之后搜索就可以了。
好像也有dfs+dp的做法,不过我觉得这个更好理解。
1、单顺子 2、双顺子 3、三顺子 4、三带一、三带二 5、四带二、四带两对
由于每张牌的数量最多有4张,所以能保证这一种牌只要有就一定能一次打出去,所以出牌总次数的上限就是手牌的种类数。而每一次dfs都有一个新的上限,就是当前步数+现有的手牌的种类数。dfs过程中要进行最优化剪枝。
2016.10.28update
这题比较好理解,不过忘得差不多干净了之后又重写了一遍。
觉得现在的码风比原来好很多,所以把代码换掉_(:з」∠)_
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int T,n,x,y,ans;
int a[20];
void clear()
{
memset(a,0,sizeof(a));
ans=0;
}
bool check()
{
for (int i=1;i<=15;++i)
if (a[i]) return false;
return true;
}
void dfs(int dep)
{
if (dep>ans) return;
if (check())
{
ans=min(ans,dep);
return;
}
int sum=0;
for (int i=1;i<=13;++i)
if (a[i]) sum++;
if (a[14]+a[15]) sum++;
ans=min(ans,dep+sum);
// 1 单顺子 2 双顺子 3 三顺子 4 三带一、三带二 5 四带二、四带两对
for (int kind=1;kind<=5;++kind)
{
if (kind==1)
{
for (int i=1;i<=8;++i)
if (a[i])
{
bool flag=true;
for (int j=i+1;j<=i+3;++j)
if (!a[j]) {flag=false;break;}
if (!flag) continue;
for (int j=i+4;j<13&&a[j];++j)
{
for (int k=i;k<=j;++k) --a[k];
dfs(dep+1);
for (int k=i;k<=j;++k) ++a[k];
}
}
}
if (kind==2)
{
for (int i=1;i<=10;++i)
if (a[i]>=2&&a[i+1]>=2)
for (int j=i+2;j<13&&a[j]>=2;++j)
{
for (int k=i;k<=j;++k) a[k]-=2;
dfs(dep+1);
for (int k=i;k<=j;++k) a[k]+=2;
}
}
if (kind==3)
{
for (int i=1;i<=11;++i)
if (a[i]>=3)
{
if (a[i+1]<3) continue;
for (int j=i;j<13&&a[j]>=3;++j)
{
for (int k=i;k<=j;++k) a[k]-=3;
dfs(dep+1);
for (int k=i;k<=j;++k) a[k]+=3;
}
}
}
if (kind==4)
{
for (int i=1;i<=13;++i)
if (a[i]>=3)
{
a[i]-=3;
for (int j=1;j<=15;++j)
if (a[j])
{
--a[j];
dfs(dep+1);
++a[j];
}
for (int j=1;j<=15;++j)
if (a[j]>=2)
{
a[j]-=2;
dfs(dep+1);
a[j]+=2;
}
a[i]+=3;
}
}
if (kind==5)
{
for (int i=1;i<=15;++i)
if (a[i]>=4)
{
a[i]-=4;
for (int j=1;j<=15;++j)
if (a[j])
{
--a[j];
for (int k=j;k<=15;++k)
if (a[k])
{
--a[k];
dfs(dep+1);
++a[k];
}
++a[j];
}
a[i]+=4;
}
for (int i=1;i<=15;++i)
if (a[i]>=4)
{
a[i]-=4;
for (int j=1;j<=15;++j)
if (a[j]>=2)
{
a[j]-=2;
for (int k=j;k<=15;++k)
if (a[k]>=2)
{
a[k]-=2;
dfs(dep+1);
a[k]+=2;
}
a[j]+=2;
}
a[i]+=4;
}
}
}
}
int main()
{
scanf("%d%d",&T,&n);
while (T--)
{
clear();
for (int i=1;i<=n;++i)
{
scanf("%d%d",&x,&y);
if (!x) a[y+13]++;
if (x==1||x==2) a[x+11]++;
if (x>=3) a[x-2]++;
}
for (int i=1;i<=13;++i)
if (a[i]) ans++;
if (a[14]+a[15]) ans++;
dfs(0);
printf("%d\n",ans);
}
}
【代码】
[cpp] view
plain copy
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 50005
using namespace std;
int L,n,m,l,r,mid,num;
int a
;
int ok(int x){
int ans=0,k=0;
for (int i=1;i<=n+1;++i)
if (a[i]-a[k]<x)
ans++;
else k=i;
return ans;
}
int main(){
scanf("%d%d%d",&L,&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
a[0]=0; a[n+1]=L;
l=0; r=L;
while(l<=r){
mid=(l+r)/2;
num=ok(mid);
if (num<=m) l=mid+1;
else r=mid-1;
}
printf("%d",l-1);
}
T2 一眼就能看出来是dp,但是dp学的不好,想了一会没大有思路,就只写了30分的部分分。dfs没跑出来就只得了10分。
动规的思路看了ShallWe的代码才勉强理解,要是让我自己想是绝对想不到的,因为我当时连前缀和优化是什么都不怎么清楚。
f[i][j][k][l]表示第一个串的前i个,第二个串的前j个,分成k段,还有一维是前缀和,也可理解为选还是不选。注意这里的第一维要加一个滚动数组。
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 1005
#define M 205
#define p 1000000007
using namespace std;
char s1
,s2[M];
int n,m,k;
int f[2][M][M][2];
inline void in(){
char ch=getchar();
while (ch<'a'||ch>'z') ch=getchar();
s1[1]=ch;
for (int i=2;i<=n;++i) s1[i]=getchar();
ch=getchar();
while (ch<'a'||ch>'z') ch=getchar();
s2[1]=ch;
for (int i=2;i<=m;++i) s2[i]=getchar();
}
int main(){
scanf("%d%d%d",&n,&m,&k);
in();
f[0][0][0][0]=1;
f[1][0][0][0]=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=min(i,m);++j)
for (int l=1;l<=k;++l){
int now=i&1,last=(i-1)&1;
if (s1[i]==s2[j]){
f[now][j][l][1]=((f[last][j-1][l-1][0]+f[last][j-1][l-1][1])%p+f[last][j-1][l][1])%p;
f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;
}
else{
f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;
f[now][j][l][1]=0;
}
}
printf("%d",(f[n&1][m][k][0]+f[n&1][m][k][1])%p);
}
2016.10.27update
注意上文的“勉强理解”,今天把当时写的代码翻出来看看然后一脸懵逼,推翻重写。
可能是最近这种类型的dp做的比较多,现在看这道题就比较简单了。
同样是把代码先贴出来,详细题解戳这里
撒花撒花~~
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 1005
#define M 205
#define Mod 1000000007
int n,m,p;
int f[2]
[M],s[2]
[M],g
[M];
char a
,b[M];
void init()
{
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
g[i][j]=min(i,j);
for (int k=1;k<=min(i,j);++k)
if (a[i-k+1]!=b[j-k+1])
{
g[i][j]=k-1;
break;
}
}
}
int main()
{
scanf("%d%d%d\n",&n,&m,&p);
gets(a+1);gets(b+1);
init();
for (int i=1;i<=m;++i)
for (int j=i;j<=n;++j)
{
f[1][j][i]=f[1][j-1][i];
if (g[j][i]==i) f[1][j][i]++;
s[1][j][i]=s[1][j-1][i-1]+f[1][j][i];
}
for (int i=2;i<=p;++i)
{
memset(f[i&1],0,sizeof(f[i&1]));
memset(s[i&1],0,sizeof(s[i&1]));
for (int j=1;j<=n;++j)
for (int k=1;k<=m;++k)
{
f[i&1][j][k]=f[i&1][j-1][k];
if (g[j][k])
{
int x=s[(i-1)&1][j-1][k-1];
f[i&1][j][k]=(f[i&1][j][k]+s[(i-1)&1][j-1][k-1])%Mod;
int J=max(j-g[j][k]-1,0),K=max(k-g[j][k]-1,0);
int y=s[(i-1)&1][J][K];
f[i&1][j][k]=((f[i&1][j][k]-s[(i-1)&1][J][K])%Mod+Mod)%Mod;
}
s[i&1][j][k]=(s[i&1][j-1][k-1]+f[i&1][j][k])%Mod;
}
}
printf("%d\n",f[p&1]
[m]);
}
T3 第一眼觉得是倍增,然后各种写,越写越觉得太繁琐,好像实现不了。最后只写了一个链,时间复杂度还算错了,所以只有5分。其实还是有很多部分分的,但是考试时没有时间写了。
考完试思考了一下感觉还是做不大出来,听学长说了二分,不是很懂,下面的来自Rivendell的博客:http://www.cnblogs.com/Rivendell/p/4972055.html
总的来说,NOIP2015做的还是可以的,简单一点的题没有丢分,但是有一些思考复杂度高一点的题目没有做出来,下一步应该加强训练。
2016.10.18update 时至今日,懒惰的Po终于把自己过掉的T3贴出来。
详细题解戳这里
撒花~~
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 300005
#define sz 19
int n,m,x,y,z,Max,dfs_clock,ans;
int tot,point
,nxt[N*2],v[N*2],c[N*2];
int h
,dis
,val
,num
,tmp
,f
[sz+5];
struct hp{int x,y,lca,dis;}edge
;
void addedge(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void build(int x,int fa)
{
num[++dfs_clock]=x;
for (int i=1;i<sz;++i)
{
if ((h[x]-(1<<i))<1) break;
f[x][i]=f[f[x][i-1]][i-1];
}
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
f[v[i]][0]=x;
h[v[i]]=h[x]+1;dis[v[i]]=dis[x]+c[i];val[v[i]]=c[i];
build(v[i],x);
}
}
int lca(int x,int y)
{
if (h[x]<h[y]) swap(x,y);
int k=h[x]-h[y];
for (int i=0;i<sz;++i)
if ((1<<i)&k) x=f[x][i];
if (x==y) return x;
for (int i=sz-1;i>=0;--i)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
bool check(int mid)
{
int cnt=0,limit=0;memset(tmp,0,sizeof(tmp));
for (int i=1;i<=m;++i)
if (edge[i].dis>mid)
{
++tmp[edge[i].x];++tmp[edge[i].y];tmp[edge[i].lca]-=2;
limit=max(limit,edge[i].dis-mid);
cnt++;
}
if (!cnt) return true;
for (int i=n;i>1;--i) tmp[f[num[i]][0]]+=tmp[num[i]];
for (int i=2;i<=n;++i)
if (val[i]>=limit&&tmp[i]==cnt) return true;
return false;
}
int find()
{
int l=0,r=Max,mid,ans;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);Max+=z;
}
build(1,0);
for (int i=1;i<=m;++i)
{
scanf("%d%d",&edge[i].x,&edge[i].y);
edge[i].lca=lca(edge[i].x,edge[i].y);
edge[i].dis=dis[edge[i].x]+dis[edge[i].y]-dis[edge[i].lca]*2;
}
ans=find();
printf("%d\n",ans);
}
目录(?)[+]
Day 1
T1 直接根据题目描述的模拟就可以了,水。【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,i,j,num;
int a[50][50];
int main(){
scanf("%d",&n);
m=(n/2)+1;
a[1][m]=1;
num=1;
i=1; j=m;
while (num<n*n){
if (i==1&&j!=n) {a
[j+1]=++num; i=n; ++j;}
else if (i!=1&&j==n) {a[i-1][1]=++num; --i; j=1;}
else if (i==1&&j==n) {a[i+1][j]=++num; ++i;}
else if (i!=1&&j!=n){
if (!a[i-1][j+1]) {a[i-1][j+1]=++num; --i; ++j;}
else {a[i+1][j]=++num; ++i;}
}
}
for (int i=1;i<=n;++i){
for (int j=1;j<n;++j)
printf("%d ",a[i][j]);
printf("%d\n",a[i]
);
}
return 0;
}
T2 读了一遍题就看出来是最小环。刚开始忽略了有好几个联通块,大数据跑不对。最后写了个并查集+dfs,AC。水。
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 2100000000
#define N 200005
using namespace std;
int n,ans,y;
int f
,next
,h
;
bool b
;
inline int in(){
int x=0; char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
int find(int x){
if (x==f[x]) return x;
f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y){
int f1=find(x);
int f2=find(y);
f[f1]=f2;
}
void dfs(int x,int dep){
b[x]=true; h[x]=dep;
if (!b[next[x]])
dfs(next[x],dep+1);
else{
int k=h[x]-h[next[x]]+1;
ans=min(ans,k);
}
}
int main(){
n=in(); ans=inf;
for (int i=1;i<=n;++i)
f[i]=i;
for (int i=1;i<=n;++i){
y=in();
next[i]=y;
if (find(i)!=find(y))
merge(i,y);
else{
memset(b,0,sizeof(b));
dfs(i,1);
}
}
printf("%d",ans);
return 0;
}
T3 时限2s内存1G就知道这道题一定很神。刚开始看到了之后一点思路都没有,以为是dp但是发现状态太多压不下。而且还有多组数据一旦跑不对就挂了。30分打表。
后来听到TA大爷说就是个dfs,把每一种情况编上号之后搜索就可以了。
好像也有dfs+dp的做法,不过我觉得这个更好理解。
1、单顺子 2、双顺子 3、三顺子 4、三带一、三带二 5、四带二、四带两对
由于每张牌的数量最多有4张,所以能保证这一种牌只要有就一定能一次打出去,所以出牌总次数的上限就是手牌的种类数。而每一次dfs都有一个新的上限,就是当前步数+现有的手牌的种类数。dfs过程中要进行最优化剪枝。
2016.10.28update
这题比较好理解,不过忘得差不多干净了之后又重写了一遍。
觉得现在的码风比原来好很多,所以把代码换掉_(:з」∠)_
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int T,n,x,y,ans;
int a[20];
void clear()
{
memset(a,0,sizeof(a));
ans=0;
}
bool check()
{
for (int i=1;i<=15;++i)
if (a[i]) return false;
return true;
}
void dfs(int dep)
{
if (dep>ans) return;
if (check())
{
ans=min(ans,dep);
return;
}
int sum=0;
for (int i=1;i<=13;++i)
if (a[i]) sum++;
if (a[14]+a[15]) sum++;
ans=min(ans,dep+sum);
// 1 单顺子 2 双顺子 3 三顺子 4 三带一、三带二 5 四带二、四带两对
for (int kind=1;kind<=5;++kind)
{
if (kind==1)
{
for (int i=1;i<=8;++i)
if (a[i])
{
bool flag=true;
for (int j=i+1;j<=i+3;++j)
if (!a[j]) {flag=false;break;}
if (!flag) continue;
for (int j=i+4;j<13&&a[j];++j)
{
for (int k=i;k<=j;++k) --a[k];
dfs(dep+1);
for (int k=i;k<=j;++k) ++a[k];
}
}
}
if (kind==2)
{
for (int i=1;i<=10;++i)
if (a[i]>=2&&a[i+1]>=2)
for (int j=i+2;j<13&&a[j]>=2;++j)
{
for (int k=i;k<=j;++k) a[k]-=2;
dfs(dep+1);
for (int k=i;k<=j;++k) a[k]+=2;
}
}
if (kind==3)
{
for (int i=1;i<=11;++i)
if (a[i]>=3)
{
if (a[i+1]<3) continue;
for (int j=i;j<13&&a[j]>=3;++j)
{
for (int k=i;k<=j;++k) a[k]-=3;
dfs(dep+1);
for (int k=i;k<=j;++k) a[k]+=3;
}
}
}
if (kind==4)
{
for (int i=1;i<=13;++i)
if (a[i]>=3)
{
a[i]-=3;
for (int j=1;j<=15;++j)
if (a[j])
{
--a[j];
dfs(dep+1);
++a[j];
}
for (int j=1;j<=15;++j)
if (a[j]>=2)
{
a[j]-=2;
dfs(dep+1);
a[j]+=2;
}
a[i]+=3;
}
}
if (kind==5)
{
for (int i=1;i<=15;++i)
if (a[i]>=4)
{
a[i]-=4;
for (int j=1;j<=15;++j)
if (a[j])
{
--a[j];
for (int k=j;k<=15;++k)
if (a[k])
{
--a[k];
dfs(dep+1);
++a[k];
}
++a[j];
}
a[i]+=4;
}
for (int i=1;i<=15;++i)
if (a[i]>=4)
{
a[i]-=4;
for (int j=1;j<=15;++j)
if (a[j]>=2)
{
a[j]-=2;
for (int k=j;k<=15;++k)
if (a[k]>=2)
{
a[k]-=2;
dfs(dep+1);
a[k]+=2;
}
a[j]+=2;
}
a[i]+=4;
}
}
}
}
int main()
{
scanf("%d%d",&T,&n);
while (T--)
{
clear();
for (int i=1;i<=n;++i)
{
scanf("%d%d",&x,&y);
if (!x) a[y+13]++;
if (x==1||x==2) a[x+11]++;
if (x>=3) a[x-2]++;
}
for (int i=1;i<=13;++i)
if (a[i]) ans++;
if (a[14]+a[15]) ans++;
dfs(0);
printf("%d\n",ans);
}
}
Day2
T1 二分答案+贪心枚举,考前做过原题,但是还是调了一会,水。【代码】
[cpp] view
plain copy
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 50005
using namespace std;
int L,n,m,l,r,mid,num;
int a
;
int ok(int x){
int ans=0,k=0;
for (int i=1;i<=n+1;++i)
if (a[i]-a[k]<x)
ans++;
else k=i;
return ans;
}
int main(){
scanf("%d%d%d",&L,&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
a[0]=0; a[n+1]=L;
l=0; r=L;
while(l<=r){
mid=(l+r)/2;
num=ok(mid);
if (num<=m) l=mid+1;
else r=mid-1;
}
printf("%d",l-1);
}
T2 一眼就能看出来是dp,但是dp学的不好,想了一会没大有思路,就只写了30分的部分分。dfs没跑出来就只得了10分。
动规的思路看了ShallWe的代码才勉强理解,要是让我自己想是绝对想不到的,因为我当时连前缀和优化是什么都不怎么清楚。
f[i][j][k][l]表示第一个串的前i个,第二个串的前j个,分成k段,还有一维是前缀和,也可理解为选还是不选。注意这里的第一维要加一个滚动数组。
【代码】
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 1005
#define M 205
#define p 1000000007
using namespace std;
char s1
,s2[M];
int n,m,k;
int f[2][M][M][2];
inline void in(){
char ch=getchar();
while (ch<'a'||ch>'z') ch=getchar();
s1[1]=ch;
for (int i=2;i<=n;++i) s1[i]=getchar();
ch=getchar();
while (ch<'a'||ch>'z') ch=getchar();
s2[1]=ch;
for (int i=2;i<=m;++i) s2[i]=getchar();
}
int main(){
scanf("%d%d%d",&n,&m,&k);
in();
f[0][0][0][0]=1;
f[1][0][0][0]=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=min(i,m);++j)
for (int l=1;l<=k;++l){
int now=i&1,last=(i-1)&1;
if (s1[i]==s2[j]){
f[now][j][l][1]=((f[last][j-1][l-1][0]+f[last][j-1][l-1][1])%p+f[last][j-1][l][1])%p;
f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;
}
else{
f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;
f[now][j][l][1]=0;
}
}
printf("%d",(f[n&1][m][k][0]+f[n&1][m][k][1])%p);
}
2016.10.27update
注意上文的“勉强理解”,今天把当时写的代码翻出来看看然后一脸懵逼,推翻重写。
可能是最近这种类型的dp做的比较多,现在看这道题就比较简单了。
同样是把代码先贴出来,详细题解戳这里
撒花撒花~~
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 1005
#define M 205
#define Mod 1000000007
int n,m,p;
int f[2]
[M],s[2]
[M],g
[M];
char a
,b[M];
void init()
{
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
g[i][j]=min(i,j);
for (int k=1;k<=min(i,j);++k)
if (a[i-k+1]!=b[j-k+1])
{
g[i][j]=k-1;
break;
}
}
}
int main()
{
scanf("%d%d%d\n",&n,&m,&p);
gets(a+1);gets(b+1);
init();
for (int i=1;i<=m;++i)
for (int j=i;j<=n;++j)
{
f[1][j][i]=f[1][j-1][i];
if (g[j][i]==i) f[1][j][i]++;
s[1][j][i]=s[1][j-1][i-1]+f[1][j][i];
}
for (int i=2;i<=p;++i)
{
memset(f[i&1],0,sizeof(f[i&1]));
memset(s[i&1],0,sizeof(s[i&1]));
for (int j=1;j<=n;++j)
for (int k=1;k<=m;++k)
{
f[i&1][j][k]=f[i&1][j-1][k];
if (g[j][k])
{
int x=s[(i-1)&1][j-1][k-1];
f[i&1][j][k]=(f[i&1][j][k]+s[(i-1)&1][j-1][k-1])%Mod;
int J=max(j-g[j][k]-1,0),K=max(k-g[j][k]-1,0);
int y=s[(i-1)&1][J][K];
f[i&1][j][k]=((f[i&1][j][k]-s[(i-1)&1][J][K])%Mod+Mod)%Mod;
}
s[i&1][j][k]=(s[i&1][j-1][k-1]+f[i&1][j][k])%Mod;
}
}
printf("%d\n",f[p&1]
[m]);
}
T3 第一眼觉得是倍增,然后各种写,越写越觉得太繁琐,好像实现不了。最后只写了一个链,时间复杂度还算错了,所以只有5分。其实还是有很多部分分的,但是考试时没有时间写了。
考完试思考了一下感觉还是做不大出来,听学长说了二分,不是很懂,下面的来自Rivendell的博客:http://www.cnblogs.com/Rivendell/p/4972055.html
总的来说,NOIP2015做的还是可以的,简单一点的题没有丢分,但是有一些思考复杂度高一点的题目没有做出来,下一步应该加强训练。
2016.10.18update 时至今日,懒惰的Po终于把自己过掉的T3贴出来。
详细题解戳这里
撒花~~
[cpp] view
plain copy
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 300005
#define sz 19
int n,m,x,y,z,Max,dfs_clock,ans;
int tot,point
,nxt[N*2],v[N*2],c[N*2];
int h
,dis
,val
,num
,tmp
,f
[sz+5];
struct hp{int x,y,lca,dis;}edge
;
void addedge(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void build(int x,int fa)
{
num[++dfs_clock]=x;
for (int i=1;i<sz;++i)
{
if ((h[x]-(1<<i))<1) break;
f[x][i]=f[f[x][i-1]][i-1];
}
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
f[v[i]][0]=x;
h[v[i]]=h[x]+1;dis[v[i]]=dis[x]+c[i];val[v[i]]=c[i];
build(v[i],x);
}
}
int lca(int x,int y)
{
if (h[x]<h[y]) swap(x,y);
int k=h[x]-h[y];
for (int i=0;i<sz;++i)
if ((1<<i)&k) x=f[x][i];
if (x==y) return x;
for (int i=sz-1;i>=0;--i)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
bool check(int mid)
{
int cnt=0,limit=0;memset(tmp,0,sizeof(tmp));
for (int i=1;i<=m;++i)
if (edge[i].dis>mid)
{
++tmp[edge[i].x];++tmp[edge[i].y];tmp[edge[i].lca]-=2;
limit=max(limit,edge[i].dis-mid);
cnt++;
}
if (!cnt) return true;
for (int i=n;i>1;--i) tmp[f[num[i]][0]]+=tmp[num[i]];
for (int i=2;i<=n;++i)
if (val[i]>=limit&&tmp[i]==cnt) return true;
return false;
}
int find()
{
int l=0,r=Max,mid,ans;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);Max+=z;
}
build(1,0);
for (int i=1;i<=m;++i)
{
scanf("%d%d",&edge[i].x,&edge[i].y);
edge[i].lca=lca(edge[i].x,edge[i].y);
edge[i].dis=dis[edge[i].x]+dis[edge[i].y]-dis[edge[i].lca]*2;
}
ans=find();
printf("%d\n",ans);
}
相关文章推荐
- 2016 NOIP提高组复赛解题报告 C++
- 2014 NOIP复赛提高组 C++ 解题报告
- NOIP2015提高组复赛 解题报告
- NOIP2013 提高组复赛解题报告
- NOIP2014 提高组复赛解题报告
- NOIP2013提高组复赛 转圈游戏 解题报告
- NOIP2010提高组复赛 解题报告(C/C++)(机械翻译)(乌龟棋)(关押罪犯)(引水入城)
- Noip 2014 提高组复赛 解题报告
- NOIP2015 提高组(senior) 解题报告
- NOIP2015提高组Day2 第二题 子串 解题报告
- NOIP 2015 提高组 day2 解题报告
- NOIp2015提高组 解题报告
- NOIP2016提高组复赛 解题报告
- noip2015 提高组 解题报告
- NOIP2016提高组复赛解题报告
- NOIP2014提高组复赛解题报告
- NOIP2012 提高组复赛解题报告
- 乌龟棋 (NOIP2010)复赛 提高组 试题二 解题代码
- NOIP2015复赛提高组day2(A:跳石头 B:子串 C:运输计划)
- NOIP2011提高组 聪明的质检员(重庆一中高2018级信息学竞赛测验6) 解题报告