链接:https://ac.nowcoder.com/acm/contest/3004
A 牛牛的DRB迷宫I
已知一个地图,图上每个点有一个字母,D表示从这点只能向下走,R表示只能向右走,B表示可以向下或向下走,问从点(1,1)走向点(n,m)共有多少种走法
dp即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int n,m;
ll dp[100][100];
char s[100][100];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>s[i]+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(i==1&&j==1) dp[1][1]=1;
if(s[i][j-1]!='D') dp[i][j]=(dp[i][j]+dp[i][j-1])%mod;
if(s[i-1][j]!='R') dp[i][j]=(dp[i][j]+dp[i-1][j])%mod;
}
cout<<dp[n][m];
return 0;
}
C 牛牛的数组越位
数组下标都是从0开始的,正常说不会有负数下标,但是有时候下标为负也不会报错,有时候就会是RE
因为数组都是线性存储的二维数组也是如此,假设开一个数组 a[10][20] ,对于每次调用 a[i][j]都会计算一下
i20+j 来找到这个元素在线性存储的位置,这个数组的元素的位置是数组首地址到之后1020个元素的位置
这一段区间被数组全覆盖,如果下标是非法的但是在这个区间内 就不是RE而是UB 如果也不在这个区间内就是RE了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=1e9+7;
int n,m,t,k;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m>>k;
map<pair<int,int> ,int> mp;
int flag=0;
for(int i=0;i<k;i++)
{
int a,b,val;
cin>>a>>b>>val;
if(flag==2) continue;
int x=a*m+b;
int y=x%m;
x/=m;
mp[{x,y}]=val;
if(x<n&&x>=0&&y<m&&y>=0)
{
if(x!=a||y!=b) flag=1;
}
else flag=2;
}
if(!flag)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
cout<<mp[{i,j}]<<' ';
cout<<endl;
}
puts("Accepted");
}
else if(flag==1)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
cout<<mp[{i,j}]<<' ';
cout<<endl;
}
puts("Undefined Behaviour");
}
else puts("Runtime error");
}
return 0;
}
D牛牛与二叉树的数组存储
这个存储就是线段树的存储方式 i节点的父节点是 i/2,左孩子是i>>1,右孩子是i>>1|1
需要注意的是输出顺序是按照点的顺序输出的,不是按照下标的顺序,所以要开一个下标来记录每个点的位置
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=400010,mod=1e9+7;
int n,m,t,k;
int s[N],num[N];
int main()
{
memset(s,-1,sizeof s);
cin>>n;
for(int i=1;i<=n;i++)
cin>>s[i];
int res=0;
for(int i=1;i<=n;i++)
if(s[i]!=-1)
res++,num[s[i]]=i;
printf("The size of the tree is %d
",res);
printf("Node %d is the root node of the tree
",s[1]);
for(int i=1;i<=res;i++)
{
int j=num[i];
printf("The father of node %d is %d, the left child is %d, and the right child is %d
",s[j],s[j/2],s[j*2],s[j*2+1]);
}
return 0;
}
F 牛牛的Link Power I
计算任意两个1之间距离和
这个有很多方法,我开了一个数组保存这个i与前面的1产生的距离和,同时记录最后一个遍历到的1的位置,还有1的数量为num ,那么到下一个1的位置,这个1与前面num个1会产生多少贡献呢,可以与上一个1的贡献作比较,记这个1与上一个1的距离为想,前面共有num个1每个1与当前1的距离都是 它与上一个1的距离加x,之前的1与上一个1产生的贡献已经计算过,每个1还会多贡献一个x,共有num个x,这样当前1的贡献就算好了,就可以继续向后算了。所有的贡献相加就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=1e9+7;
int n,m,dp[N];
char s[N];
int main()
{
cin>>n>>s;
int j=-1,cnt=0,ans=0;
for(int i=0;i<n;i++)
{
if(s[i]=='1')
{
if(j==-1)//前面没有1,不需要计算
j=i,cnt++;
else
{
dp[i]=(dp[j]+cnt*(i-j))%mod;
ans=(ans+dp[i])%mod;
cnt++;
j=i; //记录当前1的位置,方便后面的1的计算
}
}
}
cout<<ans;
return 0;
}
G 牛牛的Link Power II
在上一题的基础上增加了单点修改,很明显是用线段树去做,询问的是整个字符串的的link,所以就不用询问函数了,只用输出tr[1].sum就可以了 ,那么怎么去维护呢,对于pushup操作,父区间的link等于左区间的link加右区间的link再加左右区间新产生的link。左右区间新产生的要怎么去算呢,假设左区间的1是点 i,右区间的1是点 j,对于任意 i 与 j,它俩的贡献= ri(i到左区间的右端点的距离 )+ lj( j到右区间的左端点的距离)+1,(线段树的左右区间不相邻会有距离1的空隙)。令 i 不变 j 任意,对ri+lj+1求和可以发现日是不变的,ri+1共加了cnt2个(右区间1的总个数),而对lj求和可以用一个变量来表示 lsum2(右区间区间的1到区间左端点的距离和),
所以对于某一个点i来说他的总贡献=(ri+1)cnt2+lsum2=ricnt2+cnt2+lsum2;
然后就要对任意的i求和了,每一个 i 的贡献都是上面那个式子,记cnt1是左区间1的个数,对上面的式子求和会发现 cnt2+lsum2是不变的共加了cnt1次 对任意ri求和也可以用一个变量rsum1来保存(左区间renyi1到区间右端点的距离和)所以求和后= rsum1cnt2+lsum2cnt1+cnt1*cnt2,这样就可以求得左右区间新产生的link了(因为(i,j)是无序的,这样就是结果了,先考虑右区间结果是一样的(这是我以为的…));
这样父区间的link就可以得到了,所以每个区间我们要记录四个值 sum(link),cnt(区间内点的数量),lsum(所有点到左端点的和),rsum(所有点到右端点的和)。(lsum和rsum的维护直接看代码吧)
#include<iostream>
using namespace std;
const int N=100010,mod=1e9+7;
typedef long long ll;
struct node
{
int l,r;
ll sum,cnt,lsum,rsum;
}tr[N*4];//线段树需要开4倍
int n,m;
char s[N];
void pushup(int rt)
{
tr[rt].cnt=tr[rt<<1].cnt+tr[rt<<1|1].cnt;//维护cnt
tr[rt].sum=(tr[rt<<1].sum+tr[rt<<1|1].sum+tr[rt<<1].rsum*tr[rt<<1|1].cnt+tr[rt<<1|1].lsum*tr[rt<<1].cnt+tr[rt<<1].cnt*tr[rt<<1|1].cnt)%mod;//维护sum
tr[rt].lsum=(tr[rt<<1|1].lsum+tr[rt<<1].lsum+(tr[rt<<1].r-tr[rt<<1].l+1)*tr[rt<<1|1].cnt)%mod;//维护lsum
tr[rt].rsum=(tr[rt<<1|1].rsum+tr[rt<<1].rsum+(tr[rt<<1|1].r-tr[rt<<1|1].l+1)*tr[rt<<1].cnt)%mod;//维护rsum
}
void build(int rt,int l,int r)
{
if(l==r)
{
int a=s[l]-'0';
tr[rt]={l,l,0,a,0,0};//sum,lsum,rsum都必须是0,
}
else
{
tr[rt]={l,r};
int mid= l+r >> 1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
}
void updata(int rt,int x,int k)
{
if(tr[rt].l==x&&tr[rt].r==x)
{
tr[rt].cnt=k;//更改cnt后再pushup维护即可,叶子区间的sum,lsum,rsum一直都是0
}
else
{
int mid= tr[rt].l+tr[rt].r >> 1;
if(x<=mid) updata(rt<<1,x,k);
else updata(rt<<1|1,x,k);
pushup(rt);
}
}
int main()
{
cin>>n>>s+1>>m;
build(1,1,n);
cout<<tr[1].sum%mod<<endl;
int q,pos;
while(m--)
{
cin>>q>>pos;
updata(1,pos,q%2);
cout<<tr[1].sum%mod<<endl;
}
return 0;
}
H 牛牛的k合因子数
k合因子数的定义见题目
通过打表可以发现不超过n的合因子数只与完全平方数有关,不是完全平方数的数与上一个完全平方数一样
所以枚举所有不超过n的所以完全平方数,统计并记录所有合因子的数目,就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=1e9+7;
int n,m,num[N];
bool st[N];
void init()//素筛
{
for(int i=2;i*i<=N;i++)
if(!st[i])
for(int j=2*i;j<N;j+=i)
st[j]=1;
}
int find(int x)//找x有有几个合因子
{
int cnt=0;
for(int i=1;i*i<=x;i++)
{
if(x%i==0)
{
if(x/i==i)
{
if(st[i])
cnt++;
}
else
{
if(st[i]) cnt++;
if(st[x/i]) cnt++;
}
}
}
return cnt;
}
int main()
{
init();
cin>>n>>m;
for(int i=1;i<=n;i++)
{
num[find(i)]++;
}
while(m--)
{
int k;
cin>>k;
cout<<num[k]<<endl;
}
return 0;
}
I 牛牛的汉诺塔
汉诺塔问题是一个很经典的问题,这个题打表就可以做,还需要借助一个结论 n层汉诺塔所需要的总步数是2^n-1。打表之后会发现一个很特殊的规律,n每增加1,6个结果只会有有3个会增加,剩余三个数不变;
当n为奇数时 有a->c,c->b,b-a会增加,当n偶数时其他三个增加,而且借助那个结论可以知道每次增加2^(n-1),这三个变量会基本均分 2 ^(n-1) ,但是也不会真的均分不能整除3嘛,每次都是其中一个到两个变量会多1个,当n是奇数时 a->c会多一个;当n是偶数时 a->b和b->c都会多一个(增量取余3等2)
到此为止,它的变化规律就找到了,递推就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010,mod=1e9+7;
int n,m,t,k;
int s[N],num[N][N];
void Hanoi(int n,int a,int b,int c)//题目上的汉诺塔函数,python改成c就可以了
{
if (n==1 )
num[a][c]++;//记录变量,这里的a,b,c并不是题目中的a,b,c,递归时会变顺序的
else
{
Hanoi(n-1,a,c,b);
num[a][c]++;
Hanoi(n-1,b,a,c);
}
}
ll a,b,c,d,e,f,sum;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
if(i&1)
{
ll res=pow(2,i-1);
b+=(res/3+1);
c+=(res/3);
f=c;
}
else
{
ll res=pow(2,i-1);
a+=(res/3+1);
d=a;
e+=(res/3);
}
}
printf("A->B:%lld
",a);
printf("A->C:%lld
",b);
printf("B->A:%lld
",c);
printf("B->C:%lld
",d);
printf("C->A:%lld
",e);
printf("C->B:%lld
",f);
printf("SUM:%lld",a+b+c+d+e+f);
return 0;
}
其他几个题还没补。。。。。。