【2017/9/19】【校内胡策题】【noip模拟】【总结】
2017-09-19 14:53
375 查看
似乎永远用不完的图报
第一题
此题很有复习价值,本题中有几个点重点:【1】 对于可行问题要往贪心方向思考
【2】 在想贪心方案是需要严格证明,宁可画上一定时间(但要控制再半小时以内)去证明正确性 , 找反例
【3】 在想贪心情况时 , 要落到实处 ,若觉得一个策略不对则需要通过题目特性落到实处看其是否正确;找反例时先感性想想反例出现情况,在去构造对应数据看是否能否定当前结论
在解决此题时 , 由于放入体积与增加体积,而且题目问题是是否可解,可使人想到贪心;而贪心策略中,由于要是其放得下,所以可以想到先放有贡献(增加体积)的情况 , 然后猜测是贪心顺序,以a排序?b排序?还是b-a?此时发现它最后加完的体积是一个定值 , 而其体积也在不断增加,那么应该是a越大的放到越后面能跟保险的放下,然而是否会有反例,根据其特性:v会应为b - a > 0 而不断增大,显而易见策略正确;
而此题头疼的是b - a < 0 时的策略 : 还是按照上面所述的思路 , a?,b?,b-a?,按照从大到小?从小到大?貌似从正面不好想,但有个性质时确定的:
最后的体积为定值
那么从次特性思考,倒着来想:最后的状态是:
V + b1 + b2 + b3 + … + bn >= a1 + a2 + a3 + … + an
那么最后一次选择便是选择先将左式减去对于的b , 在不等式成立的情况下在减去对应得a ,由于后面加上的是b - a < 0 , 那么每次减去一组对应的a,b时左式减右式的值会越来越大 , 所以只要保证每次删的b最小,时左式减小的尽量少,那么等式则月容易成立,所以嘛。。。反过来想 , 不就是。。。以b为标准从大到小排序。。。那么全程的贪心策略则出来了:
对于 a - b > 0 的情况 以a为标准从小到大排序
对于 a - b < 0 的情况 以b为标准从大到小排序
代码如下:
#include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 100001 using namespace std; long long T , n ; long long v; long long a , b; long long qu1_tot , qu2_tot; struct Queue{ long long a , b_a , b; Queue(){a = b_a = 0;} }qu1[M] , qu2[M]; bool cmp1(const Queue &x , const Queue &y){return x.a == y.a ? x.b_a > y.b_a : x.a < y.a;} bool cmp2(const Queue &x , const Queue &y){return x.b == y.b ? x.a > y.a : x.b > y.b;} long long read(){ long long an = 0; static char ch = '*'; while(!isdigit(ch))ch = getchar(); while( isdigit(ch))an = an * 10 + ch - '0' , ch = getchar(); return an; } int main(){ T = read(); while(T--){ n = read() , v = read(); bool bre = true; qu1_tot = 0 , qu2_tot = 0; memset(qu1 , 0 , sizeof qu1); memset(qu2 , 0 , sizeof qu2); for(int i = 1 ; i <= n ; ++i){ a = read() , b = read(); long long b_a = b - a; if(b - a >= 0)qu1[++qu1_tot].a = a , qu1[qu1_tot].b_a = b_a , qu1[qu1_tot].b = b; else qu2[++qu2_tot].a = a , qu2[qu2_tot].b_a = b_a , qu2[qu2_tot].b = b; } sort(qu1 + 1 , qu1 + qu1_tot + 1 , cmp1); sort(qu2 + 1 , qu2 + qu2_tot + 1 , cmp2); for(int i = 1 ; i <= qu1_tot && bre ; ++i){ if(qu1[i].a > v)bre = false; else v += qu1[i].b_a; } if(bre == false){ printf("No\n"); continue; } for(int i = 1 ; i <= qu2_tot && bre ; ++i){ if(qu2[i].a > v)bre = false; else v += qu2[i].b_a; }if(bre == false){ printf("No\n"); continue; } printf("Yes\n"); } }
第二题
这道题由于时500000的上线而且为区间,很容易想到二分加线段树维护,代码如下(由于卡产只能得65分)(复杂度O(n * (log^3))):#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1500001 using namespace std; int n; long long gcd(long long a , long long b){return b > 0 ? gcd(b , a%b) : a;} long long v_num[M] , v_gcd[M]; int l[M] , r[M]; long long num[M]; long long tot , ans , set[M]; struct val{ long long gcd , num; val(){gcd = 1 , num = (1 << 31) - 1;} val(long long x , long long y){gcd = x , num = y;} }; long long read(){ long long an = 0; static char ch = '*'; while(!isdigit(ch))ch = getchar(); while( isdigit(ch))an = an * 10 + ch - '0' , ch = getchar(); return an; } void build(int l_ , int r_ , int v){ if(l_ == r_){ v_num[v] = v_gcd[v] = num[l_]; return; } l[v] = l_ , r[v] = r_; int m_ = (l_ + r_) >> 1; build(l_ , m_ , v << 1); build(m_ + 1 , r_ , v << 1 | 1); v_gcd[v] = gcd(v_gcd[v << 1] , v_gcd[v << 1 | 1]); v_num[v] = min(v_num[v << 1] , v_num[v << 1 | 1]); // printf("[%d %d %I64d %I64d]\n",l_,r_,v_num[v] , v_gcd[v]); } val query(int l_ , int r_ , int qu_l , int qu_r , int v){ if(l_ >= qu_l && r_ <= qu_r)return val(v_gcd[v] , v_num[v]); int m_ = (l_ + r_) >> 1; val a , b , ret; if(qu_l <= m_ && qu_r > m_){ a = query(l_ , m_ , qu_l , qu_r , v << 1); b = query(m_ + 1 , r_ , qu_l , qu_r , v << 1 | 1); ret.gcd = gcd(a.gcd , b.gcd); ret.num = min(a.num , b.num); } else if(qu_l <= m_)ret = query(l_ , m_ , qu_l , qu_r , v << 1); else if(qu_r > m_)ret = query(m_ + 1 , r_ , qu_l , qu_r , v << 1 | 1); return ret; } bool check(int lon){ for(int i = 1 ; i <=n - lon + 1 ; ++i){ val x = query(1 , n , i , i + lon - 1 , 1); if(x.gcd == x.num)return true; } return false; } void find(int lon){ ans = lon * 1LL; for(int i = 1 ; i <=n - lon + 1 ; ++i){ val x = query(1 , n , i , i + lon - 1 , 1); if(x.gcd == x.num) set[++tot] = i; } } int main(){ freopen("point.in","r",stdin); scanf("%d",&n); for(register int i = 1 ; i <= n ; ++i){ num[i] = read(); //scanf("%I64d",&num[i]); if(num[i] == 1){ printf("1 %d\n1",n - 1); return 0; } } build(1 , n , 1); int L = 1 , R = n; while(L + 1 < R){ int M_ = (L + R) >> 1; check(M_) ? L = M_ : R = M_; } if(check(R))find(R); else find(L); printf("%I64d %I64d\n" , tot , ans - 1); for(int i = 1 ; i <= tot ; ++i)printf("%I64d ",set[i]); }
然而其实有更好的方法,由于时连续的区间,所以更具其【连续性】,那么每次维护一个以当前为ak而且是该区间最后一个数的满足条件的区间的长度和以当前为ak而且是该区间第一个数的满足条件的区间长;再来O(n)枚举即可
但是有一点需要注意,就是判重 , (一个合理区间里有多个相同的数)
例子:
4
1 1 1 1
代码如下:(复杂度O(n))#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1000001 using namespace std; int n , ans = 0 , top , vis[M]; long long qu[M]; int pre[M] , nex[M]; long long num[M]; int main(){ scanf("%d",&n); for(int i = 1 ; i <= n ; ++i)scanf("%I64d",&num[i]); for(int i = 1 ; i <= n ; ++i){ int set = i ; pre[i] = i; while(set && num[set] % num[i] == 0)set = pre[set] == set ? set - 1 : pre[set]; pre[i] = set + 1; } for(int i = n ; i >= 1 ; --i){ int set = i ; nex[i] = i; while(set < n + 1 && num[set] % num[i] == 0)set = nex[set] == set ? set + 1 : nex[set]; nex[i] = set - 1; } for(int i = 1 ; i <= n ; i++){ if(ans < nex[i] - pre[i]){ ans = nex[i] - pre[i]; memset(vis , 0 , sizeof vis); top = 0; } if(ans == nex[i] - pre[i]) if(!vis[pre[i]]){ vis[pre[i]] = 1; qu[++top] = pre[i]; } } printf("%d %d\n",top , ans); for(int i = 1 ; i <= top ; i++)printf("%I64d ",qu[i]); }
相关文章推荐
- 2017.9.17 胡策题 【题解 + 总结】【NOIP模拟】
- 2017 9.17 NOIP模拟 胡策题总结
- 10.24_NOIP模拟 总结
- NOIP模拟11.5——11.6の总结QAQ
- NOIP模拟20150904总结
- 【NOIP2016提高A组模拟9.7】总结
- [JZSC2016 Part 2]【NOIP2016提高A组模拟8.15】总结
- NOIP2016提高A组模拟9.17总结
- [JZSC2017]【NOIP2017提高组模拟6.29】总结
- NOIP2015提高组模拟8.12总结
- 【NOIP2015 10.28模拟】总结
- 【NOIP2015 11.3模拟】总结
- [JZSC2016 Part 2]【NOIP2016提高A组模拟8.17】总结
- 【NOIP模拟】20140913 题解 & 总结
- NOIP模拟2017.9.19 总结+心得
- NOIP2011模拟考试总结
- NOIP模拟10.28总结
- [2016JZSC Part 2] 【NOIP2016提高A组模拟8.11】总结
- NOIP模拟 171019总结
- [JZSC2016 Part 2]【NOIP2016提高A组模拟8.14】总结