NOIP 2017.10.24 总结+心得
来源:互联网 发布:fifaol3 超玩数据库 编辑:程序博客网 时间:2024/06/05 11:39
世界真的很大
今天的考试厄运缠身,orz
好端端的224变成了137,NOIP要真考成这样恐怕是要退役了
一些小错误和细节处理的问题,实在是。。
尽量通过模拟赛吧自己的问题测出来再即使修改
免得NOIP真的面临退役的命运233
看题先:
1。
讲道理NOIP DAY T1这个难度?反正我是不信
其实不算太难,但是真的有点打脑子
心路历程如下:
是K的倍数?想到NOIP 2016 DAY2 T1,即是说modK为0
那么处理矩阵前缀和?不行这道题可以有中间的矩阵
K不是1e9,是1e6?摆明了有文章
要么是数据结构维护值域要么就是直接开一个值域数组了
400的范围啊,n^4大暴力,n^3能过,那就是说枚举矩形的某个边界,然后根据那个数组的答案?
数组记录的是值域,而且是K以内的值,八成就是mod K之后的值。
考虑一个区域modK的值为0,就是说前后两个矩形的mod值相同!
那么用数组记录一下每个mod值出现过几次就好了
然后我天才的在每一个n^2后面加了个1e6的memset,正解被卡成暴力orz,得分100变成35orz
正解就是我这个方法了
完整代码:
#include<stdio.h>#include<cstring>using namespace std;typedef long long dnt;int n,m,K;int src[2000010],sum[500][500],mrk[500];dnt ans=0;dnt fix(dnt x){ return (x%K+K)%K;}int main(){ freopen("rally.in","r",stdin); freopen("rally.out","w",stdout); scanf("%d%d%d",&n,&m,&K); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int tmp; scanf("%d",&tmp); sum[i][j]=fix(sum[i-1][j]+sum[i][j-1]+tmp-sum[i-1][j-1]); } for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) { src[0]=1; for(int k=1;k<=m;k++) { int tmp=fix(sum[j][k]-sum[i-1][k]); mrk[k]=tmp; ans+=src[tmp],src[tmp]++; } for(int k=1;k<=m;k++) src[mrk[k]]=0; } printf("%I64d\n",ans); return 0;}/*2 3 21 2 12 1 2*/
感觉考试的时候,如果一道题一开始没什么思路,尝试把题目得到的信息全部写下来,写到纸上,看看能得出哪些二级结论,一步步扩大已知范围。
然后就是时间复杂度一定要慎重,memset之类的位置一定要特别注意,memset的复杂度是n不是1
2。
这道题一开始的思路就是贪心
因为首先想到在叶节点直接放肯定不优
肯定是能不放,就不放
关键是怎么判断放不放,就是说怎么判断不得不放的时候
如果直接从叶节点往上跳K的距离会出问题
那么就想到记录一个点离他最远的没有覆盖的点的距离
每往上跳一步,距离就++
但是这样在子树合并的时候就会出事,因为一个点放了军队之后,对于上面的点也有覆盖能力
所以再记录一个点往上跳的最大距离,每往上走一步就取最值然后–
记录一个点有没有被覆盖过,为了防止根节点漏判
大概思路就是这样
但是具体实现是有不对的地方
考试时也觉得不对,但是稍微一改就过不了样例,而且自己测了很多组数据都是对的,所以不太敢改,就这么交了
然后就只有70分
正解是什么我不知道
反正我最后A了,调的很不容易,模拟了100的样例,多谢LKQ的图解
发现我的down数组处理的其实很模糊,当时就觉得“差不多好像是这样”就写上去了
心一横就改了状态,只有-1,0,和正数,分别表示这个点被覆盖,不被覆盖,和最远点的距离
明确定义之后就容易多了
完整代码:
#include<stdio.h>#include<algorithm>#include<cstring>using namespace std;const int INF=0x3f3f3f3f;struct edge{ int v,last;}ed[10000010];int n,K,t,num=0,ans=0;int head[5000010],down[5000010],up[5000010],mrk[5000010];void add(int u,int v){ num++; ed[num].v=v; ed[num].last=head[u]; head[u]=num;}void dfs(int u,int f){ int tmp1=-INF,tmp2=-INF,tmp3=1,tmp4=0; for(int i=head[u];i;i=ed[i].last) { int v=ed[i].v; if(v==f) continue ; dfs(v,u); tmp3*=(down[v]==-1),tmp4=1; tmp1=max(tmp1,down[v]); tmp2=max(tmp2,up[v]); if(up[v]>0) mrk[u]=1; } if(tmp1==-INF) down[u]=0; else down[u]=tmp1+1; if(tmp2==-INF) up[u]=0; else up[u]=tmp2-1; if(tmp3 && tmp4 && mrk[u]) down[u]=-1; if(up[u]>=down[u] && up[u]>0 ) down[u]=-1; if(down[u]>=K) ans++,up[u]=K,down[u]=-1,mrk[u]=1;}int main(){ freopen("general.in","r",stdin); freopen("general.out","w",stdout); scanf("%d%d%d",&n,&K,&t); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(1,1); if(!mrk[1]) ans++; printf("%d\n",ans); return 0;}
哎,这样算是死在验证代码上的吧
自己觉得好像还可以就马马虎虎了事
感觉考试时一定要时刻持怀疑态度,稍有不对就要深究,一定要完全明确才行
想到稍有不对的地方要有直接动手实现和模拟的决断
这样
3。
这道题考试的时候是真的没有什么思路
只是想了一遍状压的大模拟,暴力
5分钟写完+对拍
确认无误后24分稳稳到手
后面的几个答案小于4的点rand的了8分,总分32
正解略有复杂,但想通了还是很简单
我们把原01串两两异或得到一个新的差分数列
原数列的区间翻转其实就是新数列两点翻转
01翻转考虑成1走到0的位置
不会出现00翻转的情况,因为是白忙活
11翻转考虑成一个1走到另一个1的位置,两个1碰上然后一起消失
两点翻转其实就是一个1走到一个与他相距为bi的地方
可以预处理出来每个1走到每个1最少要走多少步,即翻转多少次
那么问题就变成了一共有K个1,两两之间有一个代价(步数)
两两之间碰到会消失
问最少代价使得所有的1全部消失
K的范围很小,枚举状态就变成一个简单状压DP了
枚举状态枚举想要相遇的两个点,刷表法更新答案
O(k^2 * 2^k)
完整代码:
#include<stdio.h>#include<map>#include<cstring>#include<queue>#include<algorithm>using namespace std;const int INF=0x3f3f3f3f;pair <int,int> p[30];queue <int> state;int n,m,K,cnt=0;int dis[20][100010],a[100010],f[100010],b[100010];void sov1(){ int ste=(1<<n)-1,ans=ste; for(int i=1;i<=K;i++) { int tmp; scanf("%d",&tmp); ans^=(1<<tmp-1); } for(int i=1;i<=m;i++) scanf("%d",&b[i]); memset(f,0x3f3f3f3f,sizeof(f)); f[ste]=0; for(int i=ste;i>=0;i--) for(int j=1;j<=m;j++) for(int k=0;k<n-b[j]+1;k++) { int tmp=i^(((1<<b[j])-1)<<k); f[tmp]=min(f[tmp],f[i]+1); } printf("%d\n",f[ans]);}void SPFA(pair <int,int> S){ int x0=S.first,y0=S.second; memset(dis[x0],INF,sizeof(dis[x0])); while(!state.empty()) state.pop(); state.push(y0),dis[x0][y0]=0; while(!state.empty()) { int u=state.front(); state.pop(); for(int i=1;i<=m;i++) { if(u+b[i]<=n && dis[x0][u+b[i]]>dis[x0][u]+1) { dis[x0][u+b[i]]=dis[x0][u]+1; state.push(u+b[i]); } if(u-b[i]>=0 && dis[x0][u-b[i]]>dis[x0][u]+1) { dis[x0][u-b[i]]=dis[x0][u]+1; state.push(u-b[i]); } } }}int sov(int ste){ f[ste]=0; for(int i=ste;i>=0;i--) for(int j=0;j<cnt;j++) if(i&(1<<j)) for(int k=0;k<cnt;k++) if(j!=k && i&(1<<k)) f[i^(1<<j)^(1<<k)]=min(f[i^(1<<j)^(1<<k)],f[i]+dis[j][p[k].second]); return f[0];}void sov2(){ for(int i=1;i<=K;i++) { int tmp; scanf("%d",&tmp); a[tmp]=1; } for(int i=1;i<=m;i++) scanf("%d",&b[i]); for(int i=0;i<=n;i++) if(a[i] ^ a[i+1]) p[cnt]=make_pair(cnt,i),cnt++; for(int i=0;i<cnt;i++) SPFA(p[i]); memset(f,INF,sizeof(f)); int ans=sov((1<<cnt)-1); printf("%d\n",ans);}int main(){ freopen("starlit.in","r",stdin); freopen("starlit.out","w",stdout); scanf("%d%d%d",&n,&K,&m); if(n<=16) sov1(); else sov2(); return 0;}
这道题算是提供了一步区间转化为单点的差分思路
先看题目数据范围考虑对什么东西状压,然后考虑转化思路
今天的考试实在是很迷
要不是第一题脑残加了个memset,第二题写的时候脑子不清楚(。。。)不会这么惨
本来是信心题考成这样,我还是太弱啊
叶正好借这几次模拟考试查出自己易犯的错误然后改正,调整自己考试的状态,深化考试的套路
明天休闲字符串考试,要翻盘才好啊
嗯,就是这样
- NOIP 2017.10.24 总结+心得
- NOIP 2017.10.3 总结+心得
- NOIP 2017.10.4 总结+心得
- NOIP 2017.10.20 总结+心得
- NOIP 2017.10.23 总结+心得
- NOIP 2017.10.27 总结+心得
- NOIP 2017.9.17 总结+心得
- NOIP模拟2017.9.19 总结+心得
- NOIP模拟赛2017.9.11 考试心得+总结
- NOIP模拟 2017.10.4 总结
- noip总结
- NOIP总结
- NOIP总结
- NOIP总结
- NOIp总结
- 论蒟蒻是怎样作死的(noip模拟赛心得&易错总结)
- 2017.10.11 noip模拟赛 总结
- 2017.10.15 noip模拟赛 总结
- osmdroid API解读(七)
- 第八周【项目3-顺序串算法】
- Java代码实现两台电脑之间传文件(2)
- Combinatorics——HDUOJ 1100
- 第七周项目一—建立顺序环形队列算法库
- NOIP 2017.10.24 总结+心得
- Android使用无线连接手机调试
- Go实现图的宽度搜索
- Spring-IOC
- 对KMP算法的理解
- EXT.NET
- 病毒式营销
- spring框架访问properties配置文件的设置方法
- 代数系统