NOIP 2018 day1 题解
今年noip的题和去年绝对是比较坑的题了,但是打好的话就算是普通水准也能350分以上吧。
t1:
很显然这是一个简单的dp即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<ctime> #include<iomanip> #include<vector> #include<map> #include<queue> #include<stack> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const long long maxn=100020; long long n; long long a[maxn],now=0,ans=0; int main() { //freopen("road.in","r",stdin); //freopen("road.out","w",stdout); n=read(); for(long long i=1;i<=n;i++)a[i]=read(); for(long long i=1;i<=n;i++) { if(a[i]>now){ans+=a[i]-now;now=a[i];} else now=a[i]; } printf("%lld\n",ans); return 0; }
t2:
很显然,这是个完全背包,考试的时候沙比没看出来打了搜索。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<ctime> #include<iomanip> #include<vector> #include<map> #include<queue> #include<stack> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=200; int t,n; int a[maxn],f[25002],ans=0; int main() { //freopen("1.in","r",stdin); //freopen("money.out","w",stdout); t=read(); for(int u=1;u<=t;u++) { memset(f,0,sizeof(f)); n=read();f[0]=1;ans=0; for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++) for(int j=a[i];j<=25001;j++) f[j]+=f[j-a[i]]; for(int i=1;i<=n;i++)if(f[a[i]]==1)ans++; printf("%d\n",ans); } return 0; }
暴力55分代码如下:
#include<bits/stdc++.h> #include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<ctime> #include<queue> #include<deque> #include<bitset> #include<set> #include<vector> #include<algorithm> #include<stack> #include<map> #include<iomanip> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(long long x) { if(x==0){putchar('0');putchar('\n');return;} if(x<0)putchar('-'),x=-x; long long num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; while(num)putchar(ch[num--]); putchar('\n');return; } const long long maxn=50002; long long n,m,flag=1,flag1=2; long long lin[maxn<<1],nex[maxn<<1],ver[maxn<<1],e[maxn<<1],len=0; long long ru[maxn],s,ans=0,q[maxn<<2],h=0,t=0,vis[maxn<<1]; long long d[maxn],cnt=0,u; void add(long long x,long long y,long long z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } //赛道修建 55分做法 long long check1(long long x) { t=0;h=0; long long tot=0,num=0; q[++t]=s; memset(vis,0,sizeof(vis)); vis[s]=1; //cout<<x<<endl; while(h++<t) { long long te=q[h]; for(long long i=lin[te];i;i=nex[i]) { long long tn=ver[i]; if(vis[tn]==1)continue; q[++t]=tn; num+=e[i]; vis[tn]=1; if(num>=x)tot++,num=0; } } if(tot>=m)return 1; else return 0; } void solve1()//一条链 { for(long long i=1;i<=n;i++)if(ru[i]==1){s=i;break;} //cout<<s<<endl; //cout<<m<<endl; //cout<<ans<<endl; long long l=1,r=ans; while(l+1<r) { long long mid=(l+r)>>1; if(check1(mid)==1)l=mid; else r=mid; } if(check1(r)==1)put(r); else put(l); return; } void dp(long long x) { vis[x]=1; for(long long i=lin[x];i;i=nex[i]) { long long tn=ver[i]; if(vis[tn]==1)continue; dp(tn); cnt=max(cnt,d[x]+d[tn]+e[i]); d[x]=max(d[x],d[tn]+e[i]); } return; } void bfs() { h=0,t=0;cnt=0; memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); q[++t]=1;vis[1]=1; while(h++<t) { long long te=q[h]; for(long long i=lin[te];i;i=nex[i]) { long long tn=ver[i]; if(vis[tn]==1)continue; d[tn]=max(d[tn],d[te]+e[i]);//取max更加严谨,尽管更慢 if(d[tn]>cnt)cnt=d[tn],u=tn; q[++t]=tn;vis[tn]=1; } } //cout<<u<<endl; memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); h=0,t=0;cnt=0; q[++t]=u;vis[u]=1; //cout<<u<<endl; while(h++<t) { long long te=q[h]; for(long long i=lin[te];i;i=nex[i]) { long long tn=ver[i]; if(vis[tn]==1)continue; d[tn]=max(d[tn],d[te]+e[i]);//取max更加严谨,尽管更慢 if(d[tn]>cnt)cnt=d[tn]; q[++t]=tn;vis[tn]=1; } } put(cnt);return; } void dfs(int x) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(vis[tn]==1)continue; vis[tn]=1; d[tn]=max(d[tn],d[x]+e[i]); if(d[tn]>cnt)cnt=d[tn],u=tn; dfs(tn); } } int check2(int x) { cnt=0;int j=t+1;//双指针 for(int i=1;i<=t;i++)if(q[i]>=x){j=i;break;} cnt+=t-j+1;j--; for(int i=1;i<j;i++)if(q[i]+q[j]>=x)j--,cnt++; if(cnt>=m)return 1; return 0; } void solve2()//菊花图 { h=0,t=0; for(int i=lin[1];i;i=nex[i])q[++t]=e[i]; sort(q+1,q+1+t); //cout<<ans<<endl; //for(int i=1;i<=t;i++)cout<<q[i]<<endl; int l=1,r=ans; while(l+1<r) { int mid=(l+r)>>1; if(check2(mid)==1)l=mid; else r=mid; } if(check2(r)==1)put(r); else put(l);return; } void solve3() { memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); dp(1);put(cnt);//树形dp求树的直径 //bfs();//两次bfs求树的直径 //两次dfs求树的直径 /*memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); cnt=0;vis[1]=1;dfs(1); memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); cnt=0;vis[u]=1;dfs(u); put(cnt);*/ } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(long long i=1;i<n;i++) { long long x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); if(x+1!=y)flag=0;//一条链的情况 ru[x]++;ru[y]++; ans+=z; if(x!=1)flag1=0; } //cout<<flag1<<endl; if(flag==1){solve1();return 0;}//一条链 if(flag1==2){solve2();return 0;}//菊花图 if(m==1){solve3();return 0;}//求树的直径的情况,两遍bfs或树形dp return 0; }
55分很好写,一个直径一个二分一个双指针扫描。
可是考试的时候打了一个n^n的暴力。菜死了
正解是二分+树形dp+二分(二分的边界很重要要不卡死)
#include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<ctime> #include<queue> #include<deque> #include<bitset> #include<set> #include<vector> #include<algorithm> #include<stack> #include<map> #include<iomanip> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(long long x) { if(x==0){putchar('0');putchar('\n');return;} if(x<0)putchar('-'),x=-x; long long num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; while(num)putchar(ch[num--]); putchar('\n');return; } const long long maxn=50002; long long n,m; long long lin[maxn<<1],nex[maxn<<1],ver[maxn<<1],e[maxn<<1],len=0; int ans=0,u=0,cnt=0; int f[maxn],v[maxn];//f[i]表示以i为根节点所能拼成最多的道路。 int q[maxn<<2],t=0; //v[i]表示当前节点还剩下的最长链 void add(long long x,long long y,long long z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } //赛道修建100分做法-->>树形dp int check1(int x,int p) { int sum=0; for(int i=1,j=t;i<j;i++) { if(i==x)continue; if(j==x)j--; if(q[i]+q[j]>=p)sum++,j--; } if(sum>=u)return 1; return 0; } void dp(int x,int p,int fa) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==fa)continue; dp(tn,p,x); } t=0,u=0;;cnt=0; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==fa)continue; f[x]+=f[tn]; if(e[i]+v[tn]>=p){cnt++;continue;} q[++t]=e[i]+v[tn]; } sort(q+1,q+1+t); for(int i=1,j=t;i<j;i++)if(q[i]+q[j]>=p)u++,j--; int l=0,r=t;//再次二分枚举哪条边不用来更新v[x] while(l+1<r) { int mid=(l+r)>>1; if(check1(mid,p)==1)l=mid; else r=mid; } if(check1(r,p)==1)v[x]=q[r]; else v[x]=q[l]; f[x]+=cnt+u; } int check(int x) { memset(f,0,sizeof(f)); memset(v,0,sizeof(v)); dp(1,x,0); if(f[1]>=m)return 1; else return 0; } void wy()//闻道玉门犹被遮 { int l=1,r=ans; while(l+1<r)//二分枚举当前修建的道路长度 { int mid=(l+r)>>1; if(check(mid)==1)l=mid; else r=mid; } if(check(r)==1)put(r); else put(l); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<n;i++) { int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); ans+=z; } wy();//应将性命逐轻车 return 0; }
就这样没了 300分很简单。