关于取模 注意取模等于零!!!
T1组合数问题
关于本题:
1.组合数杨辉三角!!!杨辉第二遍卡我!!!
杨辉三角注意初始化问题!!!
2.两维数组前缀和公式·和·传递
“
首先明显地,这题我们要求组合数,那就必须用到组合数的递推公式:C(i,j)=C(i-1,j)+C(i-1,j-1),其实就是杨辉三角的递推式。
因为要满足是k的倍数,因此枚举所有范围内的组合数,对k取模就可以直接判断了。
*以下,满足条件的组合数指能被k整除的组合数(既是k的倍数的组合数个数)
不过,为了提高效率,我们可以进行进一步的优化,就是预处理出组合数从而求出所有区间的满足条件的组合数个数,这里就要用到二维前缀和。
设a[i][j]为在C([1,i](从1到i),[1,j](从1到j))内满足条件的组合数个数,初始时,在算组合数C(i,j)时对其mod k,若得0则被k整除,a[i][j]=1;在递推时,类似于一维前缀和,a[i][j]应当对于i和j分别递推,即传递a[i-1][j]和a[i][j-1]的值,由容斥原理可得:a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];这是因为a[i-1][j]和a[i][j-1]都包含了a[i-1][j-1],因此a[i-1][j-1]被加了两次,故要减去一次。
———————
作者:Forward_Star
来源:CSDN
原文:https://blog.csdn.net/weixin_39872717/article/details/78165946
版权声明:本文为博主原创文章,转载请附上博文链接!
”
“
100100分 前缀和+递推打表
大家知道,即使打过表,算法的复杂度其实还多的一维,也就是这反复查询让人难以想到,使得我死死卡在95分一整天,才又翻了题解。
前缀和,有效减少查询统计时的复杂度,每一次查询O(n)O(n)降到$O(1),绝对过的了
记住:上加左 减左上 加自己
ans[i][j]=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-1]ans[i][j]=ans[i][j−1]+ans[i−1][j]−ans[i−1][j−1]”(敲黑板)
inline void build()
{
c[0][0]=1;
c[1][0]=c[1][1]=1;
for(int i=2;i<=2000;i++)
{
c[i][0]=1;
for(int j=1;j<=i;j++)
{
c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];//前缀和。
if(!c[i][j])ans[i][j]++;//如果满足结论,计数加一。(有没有感觉很像我的玄学优化)
}
ans[i][i+1]=ans[i][i];//继承。
}
}
inline void solve()
{
t=read(),k=read();
build();
while(t--)
{
n=read(),m=read();
if(m>n)printf("%lld\n",ans[n][n]);//如果m>n,ans只会达到n,只需输出ans[n,n]就可以了。
else printf("%lld\n",ans[n][m]);
}
}
ybx’s 代码很有道理的样子……
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,t,k,ans[2100][2100];
int f[2100][2100];
int main(){
scanf("%d%d",&t,&k);
for(int i=0;i<=2000;i++) f[i][0]=1;
for(int i=1;i<=2000;i++)
for(int j=1;j<=i;j++){
f[i][j]=(f[i-1][j]+f[i-1][j-1])%k;
if(!f[i][j]) ans[i][j]=ans[i][j-1]+1;
else ans[i][j]=ans[i][j-1];
}
while(t--){
int ans1=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
if(m>i) ans1+=ans[i][i];
else ans1+=ans[i][m];
printf("%d\n",ans1);
}
return 0;
}
ans【i】【j】为第i行前j个有多少个符合要求。
T2 蚯蚓
优先队列看出来也不会写……
关键点: 发现此题中隐含的单调性.
“
首先这道题可以用合并果子的思路做, 合并果子是找到两个最小的并合并, 而这道题是找到最大的拆分.实际上是差不多的.
如何处理每次的将每只蚯蚓加一定长度呢?或许可以转变思路, 每次只有两只蚯蚓没被加其他的全部被加了, 根据运动是相互的, 除了那被切成的两只蚯蚓其他的都往正方向移动了一些, 等价于那两只往负方向移动了一些. 所以可以记录累计加的长度, 有几只没被加的就减去就好了.
发现先被切掉的蚯蚓分成的蚯蚓一定比后切掉的蚯蚓分成的蚯蚓大. 假设这两只蚯蚓分别为a,ba,b,其中a>ba>b.那么它被切成a_1,a_2a1,a2. t秒后, bb被切成了b_1,b_2b1,b2.此时a_1,a_2a1,a2的长度为l_{a_1}+t=pl_{a}+t,l_{a_2}+t=(1-p)l_a+tla1+t=pla+t,la2+t=(1−p)la+t.而b_1,b_2b1,b2的长度却为p(l_b+t),(1-p)(1_b+t)p(lb+t),(1−p)(1b+t), 容易看出l_{a_1}>l_{b_1},l_{a_2}>l_{b_2}la1>lb1,la2>lb2.也就是说根本不需要用一个堆来维护, 它本来就具有一定单调性.
那么就是说如果蚯蚓a_1,a_2,\cdots,a1,a2,⋯,满足a_1>a_2>\cdotsa1>a2>⋯,那么以此分成两只a_{11},a_{12},a_{21},a_{22},\cdotsa11,a12,a21,a22,⋯.那么a_{12}>a_{22}>\cdots,a_{11}>a_{21}>\cdotsa12>a22>⋯,a11>a21>⋯
那么就可以将这两堆依次存储, 加上还没被切过的蚯蚓.每次要切时在这三堆里面选择最大的, 切完再依次放回去. 所以这么做时间复杂度为O(m)O(m).再优化一下细节基本上就没问题了.
结论: 善于发现题目中隐含的单调性.
Tip:有些细节需要仔细考虑不然会很惨 ”
头文件
初始
int main() {
输入
now排序
int top;
for(int i=1;i<=m;i++) {
找最长的蚯蚓
切 ,存 ,变长 ***注意不切当前要加上sigma再切!
适时输出
}
三个数组用优先队列合并排序
输出
return 0;
}
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#define N 7000005
using namespace std;
bool cmp(const int &a,const int &b){
return a>b;
}
priority_queue<int>ans;
int cut1[N],now[N],cut2[N];
int n,m,q,u,v,t;
int sigma;
double p;
int h,h1,h2;
int t0,t1,t2;
int main() {
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
p=(double)u/v;
int tmp;
for(t0=1;t0<=n;++t0)
scanf("%d",&now[t0]);
t0--;
t1=t2=0;h=h1=h2=1;
sort(now+1,now+t0+1,cmp);
int top;
for(int i=1;i<=m;i++) {
if(h>t0) { if(cut1[h1]>cut2[h2]) top=cut1[h1++];
else top=cut2[h2++]; }
else if(now[h]>=cut1[h1]&&now[h]>=cut2[h2]) top=now[h++];
else if(cut1[h1]>=cut2[h2]&&cut1[h1]>=now[h]) top=cut1[h1++];
else top=cut2[h2++];
top+=sigma;
int a1=floor(p*(double)top),a2=top-a1;
sigma+=q;
a1-=sigma;a2-=sigma;
cut1[++t1]=a1;cut2[++t2]=a2;
if(i%t==0) printf("%d ",top);
}
putchar('\n');
for(int i=h;i<=t0;++i)ans.push(now[i]);
for(int i=h1;i<=t1;++i)ans.push(cut1[i]);
for(int i=h2;i<=t2;++i)ans.push(cut2[i]);
for(int i=1;ans.size();++i){
if(i%t==0)printf("%d ",ans.top()+sigma);
ans.pop();
}
return 0;
}
257
我要做远方忠诚的儿子和物质的短暂情人……