NOIp 2015真题模拟赛 By cellur925
果然我还是最菜的==不接受反驳==
Day1
T1:神奇的幻方
思路:直接模拟即可,由于当前放法只与上一放法有关系,用两个变量记录一下即可。10分钟内切掉==
预计得分:100分
实际得分:100分
- #include<cstdio>
- #include<algorithm>
- using namespace std;
- int n,lx,ly;
- int vis[][],a[][];
- int main()
- {
- freopen("magic.in","r",stdin);
- freopen("magic.out","w",stdout);
- scanf("%d",&n);
- a[][(n+)>>]=;
- vis[][(n+)>>]=;
- lx=;ly=(n+)>>;
- for(int i=;i<=n*n;i++)
- {
- if(lx==&&ly!=n)
- {
- a[n][ly+]=i;
- vis[n][ly+]=;
- lx=n,ly++;
- }
- else if(lx!=&&ly==n)
- {
- a[lx-][]=i;
- vis[lx-][]=;
- lx--;ly=;
- }
- else if(lx==&&ly==n)
- {
- a[lx+][ly]=i;
- vis[lx+][ly]=;
- lx++;
- }
- else if(lx!=&&ly!=n)
- {
- if(!vis[lx-][ly+])
- {
- a[lx-][ly+]=i;
- vis[lx-][ly+]=;
- lx--;ly++;
- }
- else
- {
- a[lx+][ly]=i;
- vis[lx+][ly]=i;
- lx++;
- }
- }
- }
- for(int i=;i<=n;i++)
- {
- for(int j=;j<=n;j++)
- printf("%d ",a[i][j]);
- printf("\n");
- }
- return ;
- }
magic
T2:信息传递
思路:方法有多种。可以用tarjan求最小环,或者用拓扑排序,或者用并查集。以前做过,还写了题解 。感觉这是noip第二题少有的简单题了吧。考场上写的tarjan。
预计得分:100分
实际得分:80分
(是这样的qwq 因为实在win下评测的栈小,而且用的cena栈会更小,于是就出锅了qwq,拿到洛咕上是可以满分的,而且这个栈貌似还卡了递归版拓扑排序)
- #include<cstdio>
- #include<algorithm>
- #include<stack>
- #define maxn 200090
- using namespace std;
- int n,r,tot,dfs_clock,scc_cnt,ans=;
- int head[maxn],dfn[maxn],low[maxn],size[maxn],scc[maxn];
- struct node{
- int to,next;
- }edge[maxn];
- stack<int>s;
- void add(int x,int y)
- {
- edge[++tot].to=y;
- edge[tot].next=head[x];
- head[x]=tot;
- }
- void tarjan(int x)
- {
- dfn[x]=low[x]=++dfs_clock;
- s.push(x);
- for(int i=head[x];i;i=edge[i].next)
- {
- int y=edge[i].to;
- if(!dfn[y])
- {
- tarjan(y);
- dfn[x]=min(dfn[x],dfn[y]);
- }
- else if(!scc[y]) dfn[x]=min(dfn[x],low[y]);
- }
- if(low[x]==dfn[x])
- {
- scc_cnt++;
- while()
- {
- int u=s.top();
- s.pop();
- scc[u]=scc_cnt;
- if(x==u) break;
- }
- }
- }
- int main()
- {
- freopen("message.in","r",stdin);
- freopen("message.out","w",stdout);
- scanf("%d",&n);
- for(int i=;i<=n;i++)
- scanf("%d",&r),add(i,r);
- for(int i=;i<=n;i++)
- if(!dfn[i]) tarjan(i);
- for(int i=;i<=n;i++)
- size[scc[i]]++;
- for(int i=;i<=scc_cnt;i++)
- if(size[i]!=&&size[i]<ans) ans=size[i];
- printf("%d",ans);
- return ;
- }
message
T3:斗地主
思路:我没玩过斗地主qwq,表示看到题理解游戏规则就用了好久。这貌似是个搜索,不过noip还会考裸的搜索嘛qwq(不过真的考了)。平时搜索能力就很弱,独立写出搜索的情况很少,不过最近有意在练习搜索了qwq,也已经有了自己的理解,对搜索应该还是缺乏训练。这篇写了题解(在这里)。考场上写了n=2/3/4的子任务,就是骗分了。
预计得分:30分
实际得分:20分
(是这样的qwq,n==4的情况我应该是写判断的时候出锅了,暴力都打错了。。。)
- #include<algorithm>
- #include<cstdio>
- #include<cstring>
- using namespace std;
- int T,n,no,x,ans=;
- int tong[];
- void dfs(int now)
- {
- int cnt=;
- for(int i=;i<=;i++)
- {// shunzi single
- if(tong[i]) cnt++;
- else cnt=;
- if(cnt>=)
- {
- tong[i]--,tong[i-]--,tong[i-]--,tong[i-]--;
- int k=i-cnt+;
- for(int j=i-;j>=k;j--)
- tong[j]--,dfs(now+);
- for(int j=k;j<=i;j++)
- tong[j]++;
- }
- }
- cnt=;
- for(int i=;i<=;i++)
- {// shunzi double
- if(tong[i]>=) cnt++;
- else cnt=;
- if(cnt>=)
- {
- tong[i]-=,tong[i-]-=;
- int k=i-cnt+;
- for(int j=i-;j>=k;j--)
- tong[j]-=,dfs(now+);
- for(int j=k;j<=i;j++)
- tong[j]+=;
- }
- }
- cnt=;
- for(int i=;i<=;i++)
- {// shunzi third
- if(tong[i]>=) cnt++;
- else cnt=;
- if(cnt>=)
- {
- tong[i]-=;
- int k=i-cnt+;
- for(int j=i-;j>=k;j--)
- tong[j]-=,dfs(now+);
- for(int j=k;j<=i;j++)
- tong[j]+=;
- }
- }
- for(int i=;i<=;i++)
- {// four with 2
- if(tong[i]>=)
- {
- tong[i]-=;
- for(int j=;j<=;j++)
- if(tong[j])
- {//2 single
- tong[j]--;
- for(int k=;k<=;k++)
- if(tong[k])
- {
- tong[k]--;
- dfs(now+);
- tong[k]++;
- }
- tong[j]++;
- }
- for(int j=;j<=;j++)
- if(tong[j]>=)
- {//2 double
- tong[j]-=;
- for(int k=;k<=;k++)
- if(tong[k]>=)
- {
- tong[k]-=;
- dfs(now+);
- tong[k]+=;
- }
- tong[j]+=;
- }
- dfs(now+);
- //three card
- tong[i]+=;
- }
- }
- for(int i=;i<=;i++)
- {
- if(tong[i]>=)
- {
- tong[i]-=;
- for(int j=;j<=;j++)
- if(tong[j])
- {//three with 1
- tong[j]--;
- dfs(now+);
- tong[j]++;
- }
- for(int j=;j<=;j++)
- if(tong[j]>=)
- {//three with 2
- tong[j]-=;
- dfs(now+);
- tong[j]+=;
- }
- dfs(now+);// 3 single
- tong[i]+=;
- }
- }
- if(tong[]==) now++;
- else if(tong[]==) now++;
- for(int i=;i<=;i++) if(tong[i]) now+=tong[i]>>;
- for(int i=;i<=;i++) if(tong[i]) now+=tong[i]&;
- ans=min(ans,now);
- }
- int main()
- {
- scanf("%d%d",&T,&n);
- while(T--)
- {
- for(int i=;i<=n;i++)
- {
- scanf("%d%d",&x,&no);
- if(x==) x=;
- else if(x==) x=;
- else if(x==) x=;
- tong[x]++;
- }
- dfs();
- printf("%d\n",ans);
- ans=;
- memset(tong,,sizeof(tong));
- }
- return ;
- }
AC-landlord
Day1 预计得分:230
实际得分:200
考场上感觉还是有点松懈,第三题没好好想,暴力还写错了,考场上一定要全身心投入啊qwq
Day2
T1:跳石头
思路:裸的二分答案。“最大距离最小”暗示着我们二分的性质,写check函数也很容易,标准的第一题难度。
预计得分:100分
实际得分:100分
(*Add:写的二分的边界细节好像还是不准,还需要多写一些二分题巩固qwq)
- #include<cstdio>
- #include<algorithm>
- using namespace std;
- int lin,n,m;
- int seq[];
- bool check(int x)
- {
- int pre=,cnt=;
- for(int i=;i<=n;i++)
- {
- if(seq[i]-pre<x) cnt++;
- else pre=seq[i];
- if(cnt>m) return ;
- }
- return ;
- }
- int main()
- {
- scanf("%d%d%d",&lin,&n,&m);
- for(int i=;i<=n;i++)
- scanf("%d",&seq[i]);
- seq[n+]=lin;
- int l=,r=lin;
- while(l<r)
- {
- int mid=(l+r+)>>;
- if(check(mid)) l=mid;
- else r=mid-;
- }
- printf("%d\n",l);
- return ;
- }
stone
T2:子串
思路:本来dp就弱,字符串也没学多少,二者加起来就mengbier了...读题没有多久就弃疗去看第三题了,结果第三题一打就是几乎三个小时...考前10分钟把k=1的暴力打了,本想苟一苟把k=2的也写上,结果时间也不够了。
预计得分:10分
实际得分:10分
正解:方案数,dp。有经验的dalao可我不是可以轻松推出:设f[i][j][k]表示A串匹配到第i位,B串匹配到第j位,用了k个子串 的方案数。并显然有f[i][j][k]=f[i-1][j-1][k-1]+f[i-1][j-1][k],但是我们冷静分析会发现这个转移是不靠谱的。
首先是它的正确性:上述转移只有在A[i]==B[j]时才成立,于是我们就遗弃掉了没匹配上的情况。
我们可以再开一个辅助转移数组,s[i][j][k]表示一定用到了当前字符A[i]的方案数,f[i][j][k]表示用或不用当前字符的方案数。
分析s数组的转移:转移的前提是A[i]==B[j],既然A[i]一定用上了,那么有独自成一串,和与前面组合成一串的两种情况。
那么有s[i][j][k]=f[i-1][j-1][k-1]+s[i-1][j-1][k]。如果不能转移,则为0.(不能忽视的一步!!后面与滚动数组相关)
再分析f数组的转移:可由使用当前字符和不使用当前字符转移过来.
那么有f[i][j][k]=s[i][j][k]+f[i-1][j][k]。
至此我们成功解决了转移的正确性。
但是显然它是会超空间的,dp的优化我们考虑状态和转移,状态没有可优化的地方,那么我们考虑转移。观察到转移的第一维只与上一值有关,我们就可以用滚动数组。然后我是第一次写滚动数组==,其实就是把第一维改成两个量pre和now,转移后交换pre和now,达到i-1的效果。
以及不要忘记赋初值。
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int M=,mod=1e9+;
- int n,m,k,dp[][][];
- char a[M],b[M];
- int main()
- {
- freopen("substring.in","r",stdin);
- freopen("substring.out","w",stdout);
- scanf("%d%d%d",&n,&m,&k);
- scanf("%s",a+);
- scanf("%s",b+);
- if(k==)
- {
- int ans=;
- for(int i=;i<=n-m+;i++)
- {
- bool flag=;
- for(int j=;j<=m;j++)
- if(a[i+j-]!=b[j]){
- flag=;break;
- }
- if(!flag)ans++;
- }
- ans%=mod;
- printf("%d\n",ans);
- return ;
- }
- else if(k==)
- {
- int ans=;
- for(int i=;i<=n-m+;i++)//枚举第一次选的开始位置
- {
- for(int len=;len<=m-;len++)//枚举第一次选了几个
- {
- int len1=m-len;
- for(int j=i+len;j<=n-len1+;j++)//枚举第二次从哪儿开始选
- {
- bool flag=;
- int p=;
- for(int t=i;t<=i+len-;t++)
- {
- if(a[t]!=b[++p]){
- flag=;break;
- }
- }
- if(flag)continue;
- for(int t=j;t<=j+len1-;t++)
- {
- if(a[t]!=b[++p]){
- flag=;break;
- }
- }
- if(!flag){
- ans++;
- //printf("%d %d %d %d\n",i,i+len-1,j,j+len1-1);
- }
- }
- }
- }
- cout<<ans<<endl;
- return ;
- }
- else
- {
- for(int i=;i<=k;i++)
- dp[][][i]=;
- for(int i=;i<=n;i++)
- dp[i][][]=;
- for(int i=;i<=m;i++)
- dp[][i][]=;
- for(int i=;i<=n;i++)
- for(int j=;j<=m;j++)
- for(int t=;t<=k;t++)
- {
- if(a[i]==b[j])
- dp[i][j][t]=((ll)dp[i][j][t]+dp[i-][j-][t]+dp[i-][j][t-])%mod;
- else dp[i][j][t]=((ll)dp[i][j][t]+dp[i-][j][t-])%mod;
- printf("%d %d %d %d\n",i,j,t,dp[i][j][t]);
- }
- cout<<dp[n][m][k]<<endl;
- return ;
- }
- }
Chemist的30分做法
- #include<cstdio>
- #include<algorithm>
- using namespace std;
- typedef long long ll;
- int n,m,k;
- ll moder=1e9+,f[][][],s[][][];
- char A[],B[];
- int main()
- {
- scanf("%d%d%d",&n,&m,&k);
- scanf("%s",A+);
- scanf("%s",B+);
- int now=,pre=;f[][][]=;
- for(int i=;i<=n;i++)
- {
- f[now][][]=;
- for(int j=;j<=m;j++)
- for(int q=;q<=k;q++)
- {
- if(A[i]==B[j]) (s[now][j][q]=s[pre][j-][q]+f[pre][j-][q-])%=moder;
- else s[now][j][q]=;
- (f[now][j][q]=f[pre][j][q]+s[now][j][q])%=moder;
- }
- swap(now,pre);
- }
- printf("%lld",f[pre][m][k]%moder);
- return ;
- }
substring
T3:运输计划
考场思路:这题我跟它耗了将近三小时qwq.
心路历程如下:(考场上真实记录)
其实感觉m==1的情况还是比较悬的
要是最短路有多条 ,然后最后找到的那条上的最大值恰好比较小 那不就凉了么==然后到3000的数据,开始想n^2算法应该能想出来,后来感觉好像需要n^2logn
dij的复杂度是多少来着???nlogn?????如果是nlogn海星 不是就凉了==
dij好像更稳一些,但是我一共也没打过几次dij==
不对,打完dij感觉好像根本不是那么回事 又给删了==
dij求的是单源最短路 复杂度岂不会变成n^3logn???
还不如用floyd呢(滑稽)那这样好像要用lca了==
LCA复杂度多少来着???好像想出了正解(??)
但是感觉悬啊
60分差不多吧...
最后当然是非正解了==
预计得分 :0~100分
实际得分 :10分
- #include<cstdio>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<vector>
- using namespace std;
- typedef long long ll;
- int n,m,tot,t,x,y,z,noww,chang;
- ll odd,ans;
- int head[],cnt[],d[],f[][];
- struct node{
- int to,val,next;
- }edge[];
- queue<int>q;
- vector<int>son[];
- void add(int x,int y,int z)
- {
- edge[++tot].to=y;
- edge[tot].next=head[x];
- head[x]=tot;
- edge[tot].val=z;
- }
- void init()
- {
- q.push();d[]=;
- while(!q.empty())
- {
- int x=q.front();
- q.pop();
- for(int i=head[x];i;i=edge[i].next)
- {
- int y=edge[i].to;
- if(d[y]) continue;
- d[y]=d[x]+;
- f[y][]=x;
- for(int j=;j<=t;j++)
- f[y][j]=f[f[y][j-]][j-];
- q.push(y);
- }
- }
- }
- int lca(int x,int y)
- {
- if(d[x]>d[y]) swap(x,y);
- for(int i=t;i>=;i--)
- if(d[f[y][i]]>=d[x]) y=f[y][i];
- if(x==y) return x;
- for(int i=t;i>=;i--)
- if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
- return f[x][];
- }
- bool work(int fa,int x)
- {
- for(int i=head[fa];i;i=edge[i].next)
- if(edge[i].to==x)
- {
- cnt[i]++;
- son[noww].push_back(i);
- return ;
- }
- for(int i=head[fa];i;i=edge[i].next)
- {
- int y=edge[i].to;
- if(y==x)
- {
- cnt[i]++;
- son[noww].push_back(i);
- break;
- return ;
- }
- if(work(y,x))
- {
- cnt[i]++;
- son[noww].push_back(i);
- return ;
- }
- }
- return ;
- }
- int main()
- {
- freopen("transport.in","r",stdin);
- freopen("transport.out","w",stdout);
- scanf("%d%d",&n,&m);
- t=log2(n)+;
- for(int i=;i<=n-;i++)
- {
- scanf("%d%d%d",&x,&y,&z);
- add(x,y,z),add(y,x,z);
- }
- init();
- for(int i=;i<=m;i++)
- {
- scanf("%d%d",&x,&y);
- noww=i;
- int fa=lca(x,y);
- if(fa!=x) work(fa,x);
- if(fa!=y) work(fa,y);
- }
- for(int i=;i<=tot;i+=)
- cnt[i]=cnt[i]+cnt[i+];
- for(int i=;i<=tot;i+=)
- {
- ll tmp=cnt[i];
- tmp*=edge[i].val;
- if(tmp>odd) chang=i,odd=tmp;
- }
- for(int i=;i<=m;i++)
- {
- ll tmp=;
- for(int j=;j<son[i].size();j++)
- if(son[i][j]==chang||son[i][j]==chang+) continue;
- else tmp+=edge[son[i][j]].val;
- ans=max(ans,tmp);
- }
- printf("%d",ans);
- return ;
- }
考场代码 10pts
但是后来努力学习了一下60分做法(开始是想搞到 60分的)
60分做法:50pts n² + 10pts m==1,这些方法就是暴力向上跳。
然后暴力的核心思想:因为这是在树上,所以每个点可以认为有唯一的入度,所以可以预处理出到每个点的边的权值,以及这个点的父亲。
瞎搞vis数组开的3000,数组还越界了...导致有两个骗分点一直输出0.
- #include<cstdio>
- #include<algorithm>
- #define maxn 100090
- using namespace std;
- typedef long long ll;
- int n,m,tot;
- int head[maxn],d[maxn],fa[maxn],pre[maxn],vis[][],fin[maxn];
- struct node{
- int to,next,val;
- }edge[*maxn];
- struct cellur{
- int x,y;
- }tas[];
- ll lmax(ll a,ll b)
- {
- if(a>b) return a;
- else return b;
- }
- void add(int x,int y,int z)
- {
- edge[++tot].to=y;
- edge[tot].next=head[x];
- head[x]=tot;
- edge[tot].val=z;
- }
- void dfs(int x)
- {
- d[x]=d[fa[x]]+;
- for(int i=head[x];i;i=edge[i].next)
- {
- int y=edge[i].to;
- if(y==fa[x]) continue;
- pre[y]=edge[i].val;
- fa[y]=x;
- dfs(y);
- }
- }
- int subw(int pos,int x,int y)
- {
- int ans=;
- if(d[x]<d[y]) swap(x,y);
- while(d[x]>d[y])
- {
- ans+=pre[x];
- vis[x][pos]=;
- x=fa[x];
- }
- while(x!=y)
- {
- ans+=pre[x]+pre[y];
- vis[x][pos]=,vis[y][pos]=;
- x=fa[x],y=fa[y];
- }
- return ans;
- }
- void work1()
- {
- int odd=,ans=;
- int x=tas[].x,y=tas[].y;
- if(d[x]<d[y]) swap(x,y);
- while(d[x]>d[y])
- {
- ans+=pre[x];
- odd=lmax(odd,pre[x]);
- x=fa[x];
- }
- while(x!=y)
- {
- ans+=pre[x]+pre[y];
- odd=lmax(odd,pre[x]);
- odd=lmax(odd,pre[y]);
- x=fa[x],y=fa[y];
- }
- printf("%lld",ans-odd);
- }
- void work2()
- {
- int odd=;
- int minn=-;
- for(int i=;i<=m;i++) fin[i]=subw(i,tas[i].x,tas[i].y),minn=max(minn,fin[i]);
- for(int i=;i<=n;i++)
- {
- //所有路径只能是pre[i]上的 所以枚举这些边就行
- int tmp=;
- for(int j=;j<=m;j++)
- tmp=max(tmp,fin[j]-pre[i]*vis[i][j]);
- //vis数组就是这条边有没有在j这个计划中出现过 只能为0或1
- if (minn==-||minn>tmp)minn=tmp;
- }
- printf("%d",minn);
- }
- int main()
- {
- scanf("%d%d",&n,&m);
- for(int i=;i<=n-;i++)
- {
- int x=,y=,z=;
- scanf("%d%d%d",&x,&y,&z);
- add(x,y,z),add(y,x,z);
- }
- for(int i=;i<=m;i++)
- scanf("%d%d",&tas[i].x,&tas[i].y);
- fa[]=;
- dfs();
- if(m==)
- work1();
- else work2();
- return ;
- }
努力骗到的60pts
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- using namespace std;
- const int M=3e5+,MAX=1e7;
- typedef long long ll;
- int read()
- {
- int ans=;
- char ch=getchar(),last=' ';
- while(ch<''||ch>'')
- {last=ch;ch=getchar();}
- while(ch>=''&&ch<='')
- {ans=ans*+ch-'';ch=getchar();}
- if(last=='-')ans=-ans;
- return ans;
- }
- int n,m;
- ll d[M],ans=1e12;
- int num=,head[M];
- struct node{
- int beg,end,next,w;
- }e[M*];
- struct Node{
- int str,fin,cost;
- }P[M];
- struct PLAN{
- int from,to;
- }p[M];
- void add(int x,int y,int z)
- {
- num++;
- e[num].beg=x;
- e[num].end=y;
- e[num].w=z;
- e[num].next=head[x];
- head[x]=num;
- }
- int size[M],fa[M],son[M],dep[M];
- void dfs1(int x)
- {
- dep[x]=dep[fa[x]]+;
- size[x]=;
- for(int i=head[x];i;i=e[i].next)
- {
- int y=e[i].end;
- if(y==fa[x])continue;
- fa[y]=x;
- d[y]=(ll)d[x]+e[i].w;
- dfs1(y);
- size[x]+=size[y];
- if(!son[x]||size[son[x]]<size[y])
- son[x]=y;
- }
- }
- int top[M];
- void dfs2(int x,int topfa)
- {
- top[x]=topfa;
- if(!son[x])return;
- dfs2(son[x],topfa);
- for(int i=head[x];i;i=e[i].next)
- {
- int y=e[i].end;
- if(y==son[x]||y==fa[x])continue;
- dfs2(y,y);
- }
- }
- int LCA(int x,int y)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]<dep[top[y]])swap(x,y);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y])swap(x,y);
- return x;
- }
- bool cmp(Node X,Node Y)
- {
- return X.cost>Y.cost;
- }
- int main()
- {
- freopen("transport.in","r",stdin);
- freopen("transport.out","w",stdout);
- n=read();m=read();
- for(int i=;i<=n-;i++)
- {
- int x=read(),y=read(),z=read();
- add(x,y,z);add(y,x,z);
- P[i].str=x;P[i].fin=y;P[i].cost=z;
- }
- for(int i=;i<=m;i++)
- p[i].from=read(),p[i].to=read();
- dfs1();
- dfs2(,);
- if((n<=&&m<=)||m==){
- //O(nmlogn)枚举暴力,期望得分:55
- for(int i=;i<=num;i+=)//枚举改造了哪个航道
- {
- int x=e[i].beg,y=e[i].end,A;
- if(dep[x]<dep[y])A=x;
- else A=y;
- ll mx=;
- for(int j=;j<=m;j++)
- {
- int B=p[j].from,E=p[j].to;
- int lca=LCA(B,E);
- ll tim=d[B]+d[E]-*d[lca];
- if((LCA(A,B)==A||LCA(A,E)==E)&&dep[lca]<=dep[A])
- tim-=e[i].w;
- mx=max(mx,tim);
- }
- ans=min(ans,mx);
- }
- cout<<ans<<endl;
- }
- else{
- sort(P+,P+n,cmp);
- for(int i=;i<=min(n-,(MAX/m));i++)//枚举改造了哪个航道
- {
- int x=P[i].str,y=P[i].fin,A;
- if(dep[x]<dep[y])A=x;
- else A=y;
- ll mx=;
- for(int j=;j<=m;j++)
- {
- int B=p[j].from,E=p[j].to;
- int lca=LCA(B,E);
- ll tim=d[B]+d[E]-*d[lca];
- if((LCA(A,B)==A||LCA(A,E)==E)&&dep[lca]<=dep[A])
- tim-=P[i].cost;
- mx=max(mx,tim);
- }
- ans=min(ans,mx);
- }
- cout<<ans<<endl;
- }
- fclose(stdin);fclose(stdout);
- return ;
- }
Chemist的70pts骗分法
正解:二分答案+lca+树上差分
一句话题意:给你许多树上的链,要求把树上其中一条边变为0后链权值的最大值最小。
这个题出的二分还是十分隐秘的qwq,(比如zhanggenchen篱落疏疏一径深那题)因为题目要我们求计划最大值最小,所以满足二分单调性。
我们就可以二分这个最终的答案。设这个答案为mid,则所有长度>mid的路径上都至少需要删除一条边,对这些路径求交,最优方案是删去路径中长度最大的边。如果删去这条最长边后最长路径还有大于mid的,那么这个答案不合法。
问题的关键转化为求树上路径交,我们可以使用树上差分(现学的)。
另外这里预处理各个计划的链长我用到的是树上倍增求LCA+dfs。
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<queue>
- #include<cmath>
- #define maxn 300090
- // long long?
- using namespace std;
- int n,m,t,tot,maxlen,l,r,num,ret;
- int pre[maxn],head[maxn],d[maxn],f[maxn][],val[maxn],vis[maxn],dis[maxn];
- struct node{
- int to,next,val;
- }edge[maxn*];
- struct cellur{
- int x,y,len,lca;
- }tas[maxn];
- void add(int x,int y,int z)
- {
- edge[++tot].to=y;
- edge[tot].next=head[x];
- head[x]=tot;
- edge[tot].val=z;
- }
- void LCA_prework()
- {
- queue<int>q;
- q.push();d[]=;
- while(!q.empty())
- {
- int u=q.front();q.pop();
- for(int i=head[u];i;i=edge[i].next)
- {
- int v=edge[i].to;
- if(d[v]) continue;
- d[v]=d[u]+;
- f[v][]=u;
- pre[v]=edge[i].val;
- for(int j=;j<=t;j++)
- f[v][j]=f[f[v][j-]][j-];
- q.push(v);
- }
- }
- }
- int LCA(int x,int y)
- {
- if(d[x]>d[y]) swap(x,y);
- for(int i=t;i>=;i--)
- if(d[f[y][i]]>=d[x]) y=f[y][i];
- if(x==y) return x;
- for(int i=t;i>=;i--)
- if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
- return f[x][];
- }
- void dfs(int x)
- {
- vis[x]=;
- for(int i=head[x];i;i=edge[i].next)
- {
- int y=edge[i].to;
- if(vis[y]) continue;
- dis[y]=dis[x]+edge[i].val;
- dfs(y);
- }
- }
- void review(int u,int fa)
- {
- for(int i=head[u];i;i=edge[i].next)
- {
- int v=edge[i].to;
- if(v==fa) continue;
- review(v,u);
- val[u]+=val[v];
- }
- if(val[u]==num&&pre[u]>ret)
- ret=pre[u];//记录路径中最长的边
- }
- bool check(int x)
- {
- memset(val,,sizeof(val));
- num=ret=;
- for(int i=;i<=m;i++)
- {
- if(tas[i].len>x)
- {
- val[tas[i].x]++;
- val[tas[i].y]++;
- val[tas[i].lca]-=;
- num++;
- }
- }
- review(,);
- if(maxlen-ret>x) return ;
- return ;
- }
- int main()
- {
- scanf("%d%d",&n,&m);
- t=log2(n)+;
- for(int i=;i<=n-;i++)
- {
- int x=,y=,z=;
- scanf("%d%d%d",&x,&y,&z);
- add(x,y,z);add(y,x,z);
- }
- for(int i=;i<=m;i++)
- scanf("%d%d",&tas[i].x,&tas[i].y);
- LCA_prework();
- for(int i=;i<=n;i++)
- if(!vis[i]) dfs(i);
- for(int i=;i<=m;i++)
- {
- int fa=LCA(tas[i].x,tas[i].y);
- tas[i].lca=fa;
- tas[i].len=dis[tas[i].x]+dis[tas[i].y]-*dis[fa];
- maxlen=max(maxlen,tas[i].len);
- }
- r=maxlen;
- while(l<r)
- {
- int mid=(l+r)>>;
- if(check(mid)) r=mid;
- else l=mid+;
- //printf("%d %d\n",ret,num);
- }
- printf("%d",l);
- return ;
- }
(但是不开longlong好像也没关系的样子...)
Day2 预计得分:100+10+0~100=110~210
实际得分:100+10+10=120
感觉考场上不能像我今天一样再孤注一掷写T3正解了吧...,拿个稳妥的暴力分也是好的呀...。所以今天的时间分配出现了很大问题,第二题虽然我dp很不好应该至少还能加上20分k=2的情况吧,实在布星T3把所以m==1的情况都打出来,顺便再打个n==100的情况也比我现在的结果强啊qwq。考试策略和技巧还有待改善。
扯些别的:
现在感觉学习了那么多算法,目的当然是尽量在考场上打出正解。但是事实是,T2正解都很难想全,T3正解就更难了。OI赛制中暴力分,部分分还是王道。学习那么多算法,也是让我们在考场上掌握更多优雅的暴力方法,得到更多的分啊。比如ChemistDay2T3打了一个大概是树剖的东西吧,搞到了60pts(?,而我不会树剖,更不会从树的链角度出发分析==
然后感觉现在基础并不牢固==一些算法掌握的也不牢==比如滚动数组优化当初学长讲了但没写,搜索缺少方法,动规更是一塌糊涂==。对照学长给的noip知识表好像没有多少算法敢说自己掌握的特别牢==,比如树上倍增/差分。
还有一些实用技巧也并不会==,比如生成数据手打就有时候不会,(已加入todolist),有的算法复杂度不确定等等==
NOIp 2015真题模拟赛 By cellur925的更多相关文章
- NOIp2017真题模拟赛 By cellur925
果然我还是最菜的==不接受反驳 (先考了day2喵喵喵) Day2 T1:奶酪 期望得分:100分 实际得分:100分 考察:并查集 思路:这题其实之前做过了==.思路还是比较清晰的,读入时预处理出可 ...
- 9-26模拟赛 By cellur925
1.计数 (count.cpp/c/pas)时间限制:1s内存限制:256MB[问题描述]给出 m 个数 a[1],a[2],…,a[m]求 1~n 中有多少数不是 a[1],a[2],…,a[m]的 ...
- 2018.12.30【NOIP提高组】模拟赛C组总结
2018.12.30[NOIP提高组]模拟赛C组总结 今天成功回归开始做比赛 感觉十分良(zhōng)好(chà). 统计数字(count.pas/c/cpp) 字符串的展开(expand.pas/c ...
- NOIP第7场模拟赛题解
NOIP模拟赛第7场题解: 题解见:http://www.cqoi.net:2012/JudgeOnline/problemset.php?page=13 题号为2221-2224. 1.car 边界 ...
- NOIP提高真题整理(2011-2018)-标签
加粗的后面应该会有相应的简单解析(如果没咕的话:)). 2011 day1 T1:铺地毯:逆着铺 T2:选择客栈:按颜色分类枚举+二分答案 T3:Mayan游戏:大模拟dfs+剪枝 day2 T1:计 ...
- 2019.3.16 noiac的原题模拟赛
RT,这太谔谔了,我不承认这是模拟赛 但是虽然是搬了三道题,题目本身也还能看,就这么着吧 (怎么机房里就我一道原题都没做过啊 T1 CF24D Broken Robot 比较简单地列出式子之后,我们发 ...
- [NOIP 2014复习]第三章:动态规划——NOIP历届真题回想
背包型动态规划 1.Wikioi 1047 邮票面值设计 题目描写叙述 Description 给定一个信封,最多仅仅同意粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定全部的邮票数量都 ...
- 2015.9.11模拟赛 codevs 4159【hzwer的迷の数列】
题目描述 Description hzwer找了一个人畜无害的迷の数列…… 现在hzwer希望对这个数列进行一些操作,请你来回答hzwer的问题. 操作一:查询第i个数的大小 操作二:把第i个数的大小 ...
- 2017.07.11【NOIP提高组】模拟赛B组
Summary 今天的比赛打得还不错,第一题被同桌灌输的贪心,纯模拟洗脑了,然后steal的看了一下,发现怎么也对不了,一直在检查.最后10分钟才找出反例,推出动态规划方程,没有想到怎么转移,比赛就结 ...
随机推荐
- 【DataStructure】Description and Introduction of Tree
[Description] At ree is a nonlinear data structure that models a hierarchical organization. The char ...
- 【转载】FAT12文件系统之引导扇区结构
FAT12文件系统之引导扇区结构 文件系统即文件管理系统,是操作系统的重要组成部分之一,如果需要开发底层磁盘驱动或编写自己的操作系统,就必须详细了解文件系统. FAT12是Microsoft公司DOS ...
- php利用cookie防止重复提交解决办法
原理:如果数据通过了上边的两次验证,说明数据是合法有效的数据,这时候我们把提交的数据串接为一个字符串,并用MD5加密后得到一个MD5的值. 接着我们把这个值通过Cookie放进客户端,当用户下一次提交 ...
- eclipse无法启动问题记录
几天没开eclipse,居然报错“can not unload……”,搜索答案发现没有准确的,遵从了一个多人顶赞的办法重下eclipse,把配置文件拷贝一份,结果悲剧了,虽然能够打开了,但我之前配置的 ...
- Liunx之Lamp搭建笔记
1:LAMP源代码搭建用户关系 a. apache服务以daemon用户的处理请求.以root身份作为主进程. b. php源代码安装,会在httpd.conf文件里自己主动增加调用模块.可是在该文 ...
- Codeforces 8VC Venture Cup 2016 - Elimination Round F. Group Projects 差分DP*****
F. Group Projects There are n students in a class working on group projects. The students will div ...
- [翻译]Unity中的AssetBundle详解(三)
构建AssetBundles 在AssetBundle工作流程的文档中,我们有一个示例代码,它将三个参数传递给BuildPipeline.BuildAssetBundles函数.让我们更深入地了解我们 ...
- design.js
//模块式开发 var myNamespace = (function () { var myPrivateVar = 0; var myPrivateMethod = function (foo) ...
- ES6 一些新特性的总结
一.箭头函数 ES6中新增了一个箭头函数 ()=>,箭头函数通俗点讲就是匿名函数.箭头函数还有不同点在于改变函数中this,和js中的.bind 的方法差不多,继承后指向的不是最新的函数, ...
- linux初级学习笔记五:bash特性详解!(视频序号:03_2,3)
本节学习的命令:history,alias,ualias,\CMD 本节学习的技能: bash的特性 光标跳转 查看命令历史 命令历史的使用技巧 给命令起别名 命令替换 文件名通配符 shell: ...