NOIP 模拟题 简单题 随便做 题解与总结
2017-10-17 15:01
513 查看
简单题”题解
T1 简题解:
水题啊,排个序取奇数位的数就可以了。
代码:
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const ll size = 100005; ll n,ans = 0; ll da[2*size];//数组大小啊!!! int main() { scanf("%lld",&n); for (ll i = 1;i <= 2 * n;i++) scanf("%lld",&da[i]); sort(da + 1 , da + 1 + 2*n); for (ll i =1;i<=2*n;i+=2) ans += da[i]; printf("%lld",ans); return 0; }
T2 单
题解:
不难啊,考虑到一条边的两端的点x,y的b值(设x为y的父节点),可以得出其差值为∑n1a[i]−∑y的子树a[i]那么就好做了。首先,若知道a数组,则可以用此公式,先暴力算出根的b,然后用这个公式不停向下更新。若知道b,那么考虑叶节点y,其∑y的子树a[i]恰好为其本身,然后就可以用一个只关于∑1na[i]的式子表示,然后把其更新到父节点,然后一直更新到根节点,然后根节点的子树又为∑0na[i]就可以解方程啦,哈哈,只用两次dfs。
代码(点不进去了,就用了标程):
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=100010; ll T,t,n,edgenum,head ,c ,fa ,dep ,atot,ans ,v ; struct edge { int v,next; } e[N<<1]; struct node { ll tot,con; } tr ; ll read() { ll 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; } void addedge(int f,int t) { e[++edgenum].v=t; e[edgenum].next=head[f]; head[f]=edgenum; } void dfs1(int a,int pre) { fa[a]=pre; dep[a]=dep[pre]+1; for(int i=head[a]; i; i=e[i].next) { if(e[i].v==pre) continue; dfs1(e[i].v,a); } } inline void solve1() { ll root_tot=0,root_con=0,sum=0; dep[0]=-1; dfs1(1,0); for(int i=2; i<=n; i++) { tr[i].con=c[fa[i]]-c[i]; tr[i].tot=1; tr[fa[i]].tot--; tr[fa[i]].con-=tr[i].con; } for(int i=2; i<=n; i++) { root_tot+=tr[i].tot*dep[i]; root_con+=tr[i].con*dep[i]; } atot=(2*c[1]-root_con)/root_tot; for(int i=2; i<=n; i++) ans[i]=(tr[i].con+tr[i].tot*atot)/2,sum+=ans[i]; ans[1]=atot-sum; } void dfs2(int a,int pre) { fa[a]=pre; dep[a]=dep[pre]+1; for(int i=head[a]; i; i=e[i].next) { if(e[i].v==pre) continue; dfs2(e[i].v,a); v[a]+=v[e[i].v]; } } void dfs3(ll sum,int a,int pre) { ans[a]=(sum-(v[a]<<1))+ans[pre]; for(int i=head[a]; i; i=e[i].next) { if(e[i].v==pre) continue; dfs3(sum,e[i].v,a); } } inline void solve2() { ll sum=0; for(int i=1; i<=n; i++) v[i]=c[i],sum+=c[i]; dep[0]=-1; dfs2(1,0); for(int i=2; i<=n; i++) ans[1]+=c[i]*dep[i]; for(int i=head[1]; i; i=e[i].next) dfs3(sum,e[i].v,1); } int main() { int a,b; T=read(); while(T--) { edgenum=0; memset(ans,0,sizeof ans); memset(head,0,sizeof head); n=read(); for(int i=1; i<n; i++) { a=read(),b=read(); addedge(a,b),addedge(b,a); } t=read(); for(int i=1; i<=n; i++) c[i]=read(); if(t) solve1(); else solve2(); for(int i=1; i<=n; i++) printf("%lld ",ans[i]); putchar('\n'); } return 0; }
T3 题
题解:
(见我另一篇博客:
http://blog.csdn.net/Demon_Rieman/article/details/78066943)
“随便做”题解
T1 随题解:
(好高级的做法) 明显我们要用dp,一开始想的二维dp,后来看存不下。所以这题只用一个一维dp就可以了。先说一下二维的,dp[i][j]表示选j次,下余i的期望,p[i]表示只选一次,选到i的期望,然后转移很明显,就是dp[i+1][j]=(∑0≤m<moddp[i][m]∗p[m])mod1000000007,这样时间复杂度是O(mod2logm)的,空间复杂度是O(mod)的
都不行,于是我们观察,发现每次都要用 来转移,并且每次转移方式都是一样的,那不就是???对啦,递归的,也就是说,实际上答案就是的次方(类似矩阵),然后就写一个伪.矩阵快速幂就可以了,这样空间,时间,可以过。(也有人写滚动数组,但我不熟啊,所以就写了一个不滚动的。但是滚动要快一倍……)
代码:
#include<cstdio> #include<cstring> using namespace std; typedef long long ll; const ll size = 1005; const ll MOD = 1000000007; ll n , m, mode; ll ans[size] , replace[size] ,t[size]; ll q_pow(ll a,ll b,ll c) { ll res = 1; while (b) { if (b%2) (res*=a)%=c; b/=2; (a*=a)%=c; } res %= c; return res; } ll inv(ll a,ll b)//b 是质数 { return q_pow(a , b - 2 ,MOD); } void mat_mult(ll *a , ll b[]) { ll tmp[size];memset(tmp,0,sizeof tmp); for (ll i = 1;i < mode;i++) for (ll j = 1;j < mode;j++) ( tmp[ (i * j) % mode ] += a[i] * b[j] )%= MOD; for (ll i = 1;i < mode;i++) a[i] = tmp[i]; } void mat_q_pow(ll *a,ll *b,ll m)//答案 mod c { a[1] = 1; while (m) { if (m%2==1) mat_mult(a,b); m/=2; mat_mult(b,b); } } int main() { scanf("%lld%lld%lld",&n,&m,&mode); for (int i = 1;i<=n;i++) { ll x; scanf("%lld",&x); t[x]++; } ll invn = inv( n , MOD); for (int i = 1;i < mode;i++) replace[i] = (t[i] * invn)%MOD ; //for (int i = 0;i<mode;i++) //printf("%lld " , replace[i]); mat_q_pow(ans , replace , m ); ll tot = 0; for (ll i= 1;i < mode;i++) tot = (tot + i * ans[i])%MOD; (tot += MOD)%=MOD; printf("%lld",tot); }
- T2 便
题解:
限制条件推一推就变成了对于每一行,其增量相同,也就是对于同一行的 mat[k][i]−mat[k][j]对于每一个k都相等(当然列也如此),所以我们只需做一个带权并查集就可以了。如何保证不是负数?只需要每次接父节点时都接小的,然后看这个加上增量是不是负的就行了。
代码:
#include <bits/stdc++.h> typedef long long ll; const ll size =200201; using namespace std; int rank[size], twoo[size], mi[size], fa[size],_r, _c, n,T; struct ob { int ri, ci, rank; bool operator <(const ob &a) const { return ci < a.ci; } }obb[size]; int findfa(int x) { if (fa[x] == x) return x; int rt = findfa(fa[x]); rank[x] += rank[fa[x]]; fa[x] = rt; return fa[x]; } int unionm(int a,int b,int ci) { int faa = findfa(a),fab = findfa(b); if (rank[faa]>rank[fab]) fa[faa] = fab; if (rank[faa] == rank[fab]) rank[faa]++; } bool merge(int a, int b, int ci) { int ra = findfa(a), rb = findfa(b); if (ra != rb) { fa[ra] = rb;rank[ra] = ci - rank[a] + rank; return 1; } else { bool yk= (rank[a] == rank[b] + ci); return yk; } } bool panding() { for(int i= 0;i<= n;i++) { fa[i] = i; rank[i] = 0; } sort(obb + 1, obb + 1 + n); for(int i= 1;i<= n - 1;i++) if(obb[i].ci == obb[i + 1].ci) if(!merge(obb[i].ri, obb[i + 1].ri, obb[i + 1].rank - obb[i].rank)) return false; memset(twoo, 0x3f, sizeof(twoo));memset(mi, 0x3f, sizeof(mi)); for(int i= 1;i<= n;i++) { int rt = findfa(obb[i].ri); twoo[rt] = min(twoo[rt], obb[i].rank + rank[obb[i].ri]); } for(int i=0;i<=_r;i++) { int rt = findfa(i); mi[rt] = min(mi[rt], -rank[i]); } for(int i=0;i<= _r;i++) if (fa[i] == i && (twoo[i] + mi[i] < 0)) return 0; return 1; } int check = 1; int main(){ scanf("%d",&T); while(T--) { check = 1; scanf("%d%d%d",&_r,&_c,&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&obb[i].ri,&obb[i].ci,&obb[i].rank); for(int i= 0;i<= n;i++) if (obb[i].rank < 0) check = 0; if (panding()&&check) printf( "Yes\n"); else printf("No\n"); } } return 0; }
[b]- T3 做
(惊天好题,这么好的题目背景,肯定要截下来)
题解:
实际是水题,膜法师肯定不会动,所以他只会膜或者喊:“苟利国家生死以,竹外桃花三两枝。”恢复,而香港记者则每次使abs(x记者−x你)与abs(y记者−y你)中较大的减少,因为要使平方和最小。模拟一遍就行了。
代码:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int T; int main() { scanf("%d",&T); int a,b; scanf("%d%d",&a,&b); while (T--) { int x1,x2,y1,y2; int c ,d; scanf("%d%d%d%d%d%d" , &x1 , &y1 , &x2 , &y2 , &c , &d); int dx = abs(x1-x2) , dy = abs(y1-y2); int tot = 0; while (dx > 0 || dy > 0) { if (c < a) c+=b; else {tot += dx*dx + dy*dy; c -= a;} if (tot >= d) break; if (dx > dy) dx--;else dy--; if (dx > dy) dx--;else dy--; } if (tot>=d) printf("NAIVE\n"); else printf("SIMPLE\n"); } return 0; }
总结:
day1T1 数组开小了,气啊。下次一定要好好注意啊,考试时可不能犯这种错误。
T2 考试时没怎么想,连30的暴力都没写,主要是对树的题还是不是很熟。
T3 我推了好久啊,但也没推出正解,知识水平还不够啊,组合数学要多学学,虽然数学我最不好的就是组合数学,最后写暴力dp还得了80分,所以应该好好调整做题时间,想很久想不出来先把暴力写了,然后把题做完再来看这道题。(这题想了2个小时,但暴力5多分钟时候就想到了,然后就这样浪费了1小时50分钟,如果拿来做第二题,还能得30分)。
day2
T1 做不来,就只写了一个20分的骗分,结果这都只骗到10分。 如果多想想还是有可能想出来的。然后改的时候居然模数搞错了,害的只有20,改了一下午才发现…….
T2 跟day1的T3一样,花了我大多时间,一开始想正解,没想出来(以前基本没用过带权并查集),然后写90分的暴力,就是强行记录差以及强行把矩阵做出来,但不知为何只有10,码力不足啊,一定是哪写错了。
T3 千古奇冤啊,我写平方时,居然写成了数学的,没想到样例还能过,然后这题又简单,就没去造数据了。这题告诉我再简单的题也要造点数据验证。
很重要的:考试时一定要注意数组大小,符号,模数。
相关文章推荐
- [题解+总结]NOIP2015模拟题2
- 【NOIP模拟题】【表达式求值】2016.11.11第三题equal题解
- 【NOIP模拟题】【最短路】【DP】2016.11.11第一题tractor题解
- 【NOIP模拟】20140809 题解 & 总结
- 【NOIP模拟题】【线段树】【树链剖分】发放粮食题解
- 【NOIP模拟】20140907 题解 & 总结
- NOIP2016 普及组 总结+题目吐槽+代码+简单题解
- 【NOIP模拟】20140817 题解 & 总结
- 【NOIP模拟题】【数学归纳法】2016.11.17 第二题 数页码 题解
- NOIP2011提高组 DAY2 题解&总结
- 【NOIP模拟】20140814 题解 & 总结
- 【NOIP模拟题】【图论】2016.11.18 第二题 心 题解
- 【NOIP模拟题】【数学归纳法】【GCD】2016.11.16 第一题 LGTB 与序列 题解
- 【NOIP模拟题】【模拟】【DP】【JOI】2016.11.14第一题 复制&粘贴2 题解
- NOIP2016 模拟赛[一中题]题解&总结
- NOIP 2015模拟赛[nodgd题]题解&总结
- [题解+总结]NOIP动态规划大合集
- Noip2016题解&总结
- 【20150912】NOIP模拟 题解 & 总结
- 【NOIP模拟】20140815 题解 & 总结