2017.3.15模拟赛题解
来源:互联网 发布:定时开关机软件下载 编辑:程序博客网 时间:2024/05/17 03:03
# 3.11小题解
–by cym
## T1 前缀?(a.cpp/c/pas)
本题是一道非常水的DP由于n只有400;O(n^3^)轻松水过
(而且只是渐进意义,远远达不到n^3^)。
30%分数
直接开n个for枚举,强行判断。
50%分数
dfs的方式枚举,会比强行暴力快。
80%分数
80%的作法已经是DP了。我们开个数组f[i][a1][a2]记录到第i位时,有a1个2 与 a2个1;0不用存的原因是i-a1-a2就是0的个数。然后我们就可以开始DP:1、先特判f[1][1][0]=1;显然只有一位的时候只能是一位的2。2、我们的第一层for循环从2开始到n,这是枚举有几位。3、第二层for循环是枚举a1到i,由于2是最多的条件,我们显然不能从1开始,必须从n/2向上取整的位置开始。3、第三层for循环是枚举a2到i-a1但是由于1要比2数量少,a1<a2也是条件之一。然后1要比0多,我们的a2不能从1开始,要从(i-a1)/2向上取整开始。最后在for循环里我们判断如果少掉一个2是满足条件的就加上f[i-1][a1-1][a2]的数量。同理如果少掉一个1是满足条件的就加上f[i-1][a1][a2-1]的数量。少掉一个0是满足条件的就加上f[i-1][a1][a2]的数量。统计答案时,要将i=n时的所有情况全加上,来两层for枚举a1与a2将f加上。在所有取和的操作时都别忘了取模。但这样的空间不够用只能拿80%。
100%分数
100%分数的作法其实就是80%分数作法的改进。我们发现第一维只有用到上一层的情况我们就可以愉快的滚存了。空间于是达到要求。
PS:太水就不贴代码了
T2 前缀!(b.cpp/c/pas)
这道题稍微有点技巧性了。我们需要求前缀和的前缀和的前缀和……当然是若干个。
观察到本题的数据范围均<=4000我们容易联想到O(n^2^)的算法,事实上就是如此。
100%分数
参照四个1的表格。(忘记怎么用markdown的表了)。直接来手码:add前 || add后------------------------------------f0| 1 | 1 | 1 | 1 || 2 | 1 | 1 | 1 |f1| 1 | 2 | 3 | 4 || 2 | 3 | 4 | 5 |f2| 1 | 3 | 6 | 10|| 2 | 5 | 9 | 14|f3| 1 | 4 | 10| 20|| 2 | 7 | 16| 30|f4| 1 | 5 | 15| 35|| 2 | 9 | 25| 55|只看add前部分我们可以发现这实际上就是第i位数对第j层第k个数所产生的贡献我们可以事先统计出这个表之后直接求第k层的第x个数时,我们直接将1到x数的贡献乘上数字的大小之和就是目前所求的答案。当然实际上的表应该倒序存,给出表的层数是打出表层数减1。如果k=0的话直接输出a[k]。add操作直接在a数组上进行。我们就完成了这道题。
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#define ll long longusing namespace std;inline ll read(){ ll 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 wri(ll x){ if(x>=10) wri(x/10); putchar(x%10+'0');}void write(ll x){ if(x<0){putchar('-'); x=-x;} wri(x);}void writeln(ll x){ write(x); puts("");}const int N=4010;const ll mod=1000000007;int n,m,q;ll a[N],p[N][N];int main(){ freopen("b.in","r",stdin); freopen("b.out","w",stdout); scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;++i) a[i]=read(); for(int i=0;i<n;++i) p[1][i]=1; for(int i=2;i<=m;++i) for(int j=0;j<n;++j) if(j!=0) p[i][j]=(p[i-1][j]+p[i][j-1])%mod; else p[i][j]=p[i-1][j]; char ch[10]; ll x,y,ans; while(q--){ scanf("%s",ch); x=read(); y=read(); if(ch[0]=='Q'){ if(x==0) writeln(a[y]); else{ ans=0; for(int i=1;i<=y;i++) ans=(ans+p[x][y-i]*a[i])%mod; writeln(ans); } } else a[x]=(a[x]+y)%mod; } return 0;}
T3前缀……(c.cpp/c/pas)
此题非常毒瘤,原为出题正好研究该类型的题目,顺便就处给我们做了……
题目内容和T2一样,不过它的数据范围发生了改变。
m<=10;n,q<=100000
嗯,非常的毒瘤我们的第二题的作法就不能用了。
首先推测时间复杂度大概为O(n m log n);
观察到m的数据范围很小,有前缀和的关系,应该会用到数据结构,猜测为树状数组。(事实说明是正确的)这里需要维护十重前缀和,就容易想到十重树状数组。这里我直接沿用学长本身的题解。
以前做过一题求前缀和的前缀和,我从那题受到启发想到了这题,但应该有比我的方
法更好的方法。
对于 15%的数据,一棵树状数组动态维护前缀和。
对于 40%的数据,两棵树状数组:一棵维护前缀和,一棵维护
Ai*(n-i+1)的和,答案
就是 Tree2[i]-Tree1[i]*(n-i)。
对于 60%和 80%的数据,其实是差一点就想到标算了,方法和标算类似,这里不再赘述。
考虑满分算法,由 15%和 40%的方法可以猜想,维护前缀和用一棵树状数组,维护前缀
和的前缀和用两棵树状数组,那维护 10 重前缀和,是不是用 10 棵树状数组呢?
如果真的
是用 10 棵树状数组,那维护什么呢?这时我们很容易想到 Ai*(n-i+1)中的(n-i+1),这个
数的意义是什么呢?它表示第 i 个数对第二重前缀和的最后一位的贡献(即加了几次)。
我们假设前四个数分别为 a、b、c、d,要维护的是三重前缀和。
有没有看出什么规律来呢?如果用 num[j][i]表示第 j 层(即第 j 重前缀和)时,Ai
对第 j 层的最后一位的贡献的话,num[j][i]=num[j][i+1]+num[j-1][i]
我们就可以很容易地求出每个数的贡献,O(n * 0)预处理出来。这样思路就明朗了:第 j 棵树状数组维护
Ai * num[j][i]的和
知道了怎么维护,接下来就是想怎么求了。我们用 Ans[j][i]表示第 j 重前缀和的第 i
个数(即答案)。同样,我们来思考 Tree2[i]-Tree1[i]*(n-i)中(n-i)的意义,它表示前
i 个数多算的贡献,我们能否也像 num[j][i]一样打出一张表 f[j][i]呢?
我们以上表为例,
进一步去寻找规律。
我们把上表的字母去掉,只剩下系数。
发现 Ans[j][i]的系数是 Ans[j][i+1]的系数的后缀。
Ans[3][2](3 1 0 0)有一种求法:
(10 6 3 1)-(4 3 2 1)=(6 3 1 0)
(6 3 1 0)-(3 2 1 0)=(3 1 0 0)
考虑到对于 Ans[j][i]来说,若 k>i,则 Ak 对 Ans[j][i]的贡献必然为 0。这样的话,
Ans[j][i]的值就只与 Tree[k]i有关了。
由于上表太小找不到规律,我再画大一点。求 Ans[5][2](5 1 0 0 0),即(5 1)。
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#define ll long long#define lowbit(x) (x&(-x))using namespace std;inline ll readll(){ ll 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 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;}inline void write(int x){ if(x<0){putchar('-'); x=-x;} if(x>=10) write(x/10); putchar(x%10+'0');}void writeln(int x){ write(x); puts("");}const int N=100100;const int mod=1000000007;int ans;int n,m,q,ch,x,y;int f[11][N],c[11][N],num[11][N];int a[N];char s[10];void add(int k,int x,int y){ for( ;x<=n;x+=lowbit(x)) c[k][x]=(c[k][x]+y)%mod;}int query(int k,int x){ int t=0; for( ;x;x-=lowbit(x)) t=(c[k][x]+t)%mod; return t;}int main(){ freopen("c.in","r",stdin); freopen("c.out","w",stdout); n=read(); m=read(); q=read(); for(int i=1;i<=n;i++) f[1][i]=i; for(int i=2;i<=10;i++) for(int j=1;j<=n;j++) f[i][j]=(f[i-1][j]+f[i][j-1])%mod; for(int i=1;i<=n;i++) num[1][i]=1; for(int i=2;i<=10;i++) for(int j=n;j;j--) num[i][j]=(num[i-1][j]+num[i][j+1])%mod; for(int i=1;i<=n;i++){ a[i]=read(); for(int j=1;j<=10;j++) add(j,i,1ll*a[i]*num[j][i]%mod); } while(q--){ scanf("%s",s); if(s[0]=='Q'){ y=read(); x=read(); if(!y) writeln(a[x]); else{ ans=query(y,x); for(int j=n-x,k=1;k<y&&j;j--,k++){ if(k&1) ans=(ans+mod-1ll*f[k][j]*query(y-k,x)%mod)%mod; else ans=(ans+1ll*f[k][j]*query(y-k,x)%mod)%mod; } writeln(ans); } } else{ x=read(); y=read(); a[x]=(a[x]+y)%mod; for(int i=1;i<=10;i++) add(i,x,1ll*y*num[i][x]%mod); } } return 0;}
- 2017.3.15模拟赛题解
- 2017.3.11模拟赛题解
- 2016.8.15C组模拟赛题解
- 3.5模拟赛题解
- 模拟赛【色子】题解--spfa
- 模拟赛【修路】题解--kruskal
- Wannafly模拟赛3 题解
- Wannafly模拟赛3 题解
- Wannafly模拟赛4 题解
- 2017.3.18NOIP模拟赛题解及反思
- 2017.3.10NOIP模拟赛题解及反思(伪)
- 2016.08.15【初中部 NOIP提高组 】模拟赛C题解
- 2016.08.15【初中部 NOIP提高组 】模拟赛C题解
- 2016.08.15【初中部 NOIP提高组 】模拟赛C题解
- 2016.09.15【初中部 NOIP提高组 】模拟赛C题解
- 2017.4.15模拟赛B组题解(简)
- 2017.04.15【NOIP2017提高组】模拟赛B组题解
- 【KpmCup#0 省选模拟赛】题解
- WEB-Listener&Filter篇
- PCA & Adaptive Color Attributes for Real-Time Visual Tracking
- 重写toString()方法
- 嵌入式开发学习笔记 ( java
- HTTP的请求报文与响应报文
- 2017.3.15模拟赛题解
- JDBC连接数据库教程,postgreSQL
- Hadoop常用命令入门
- HTTP 报文及作用
- 华为20170317实习机试答案
- 多线程初步与生产者消费者问题
- instanceof关键字
- Python2和Python3之间关于字符串编码处理的差别
- c++操作符重载