写在前面:这场的题目感觉比前两场的难,最后苟了个七题,BEJ估计明天再补吧。现在更新一下已经过了的七道题。
题意:给出一个 n ∗ m n*m n∗m的迷宫,每一格有三种情况,分别是 D D D向下移动, R R R向右移动, B B B可以向下也可以向右移动。
题解:显然的dp题。当 a [ i ] [ j ] = D a[i][j]=D a[i][j]=D时 d p [ i + 1 ] [ j ] + = d p [ i ] [ j ] dp[i+1][j]+=dp[i][j] dp[i+1][j]+=dp[i][j],当 a [ i ] [ j ] = R a[i][j]=R a[i][j]=R时 d p [ i ] [ j + 1 ] + = d p [ i ] [ j ] dp[i][j+1]+=dp[i][j] dp[i][j+1]+=dp[i][j],当 a [ i ] [ j ] = B a[i][j]=B a[i][j]=B时 d p [ i ] [ j + 1 ] + = d p [ i ] [ j ] , d p [ i + 1 ] [ j ] + = d p [ i ] [ j ] dp[i][j+1]+=dp[i][j],dp[i+1][j]+=dp[i][j] dp[i][j+1]+=dp[i][j],dp[i+1][j]+=dp[i][j]。最后输出 d p [ n ] [ m ] dp[n][m] dp[n][m]即可。
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
ll f[100][100];
int n,m;
char s[100][100];
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%s",&s[i]);
f[0][0]=1ll;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(s[i][j]=='D')f[i+1][j]=(f[i+1][j]+f[i][j])%mod;
else if(s[i][j]=='R')f[i][j+1]=(f[i][j+1]+f[i][j])%mod;
else if(s[i][j]=='B')f[i+1][j]=(f[i+1][j]+f[i][j])%mod,f[i][j+1]=(f[i][j+1]+f[i][j])%mod;
printf("%lld\n",f[n-1][m-1]);
return 0;
}
题意:题目描述了一下数组越界的概念,具体自己看题目介绍吧。
题解:按照题意模拟即可。
#include
using namespace std;
typedef long long ll;
const int maxn=2e7+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int n,m,p;
int G[1010][1010];
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&p);
int fflag=0,ffflag=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)G[i][j]=0;
while(p--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int k=m*x+y;
if(!ffflag){
if(k<0 || k>=n*m){ffflag=1;continue;}
if(x<0 || y<0 || x>=n || y>=m){fflag=1;}
int yy=k%m,xx=k/m;
G[xx][yy]=z;
}
}
if(ffflag){
puts("Runtime error");
continue;
}
for(int i=0;i<n;i++){
for(int j=0;j<m-1;j++)printf("%d ",G[i][j]);
printf("%d\n",G[i][m-1]);
}
if(fflag)puts("Undefined Behaviour");
else puts("Accepted");
}
return 0;
}
题意:题目描述了一下数组存储二叉树的概念,具体自己看题目介绍即可。
题解:按照题意模拟即可。
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int n,a[2*maxn],f[maxn*2],sum;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),f[a[i]]=i;
for(int i=1;i<=n;i++)if(a[i]!=-1)sum++;
for(int i=n+1;i<=n*2+1;i++)a[i]=-1;
a[0]=-1;
printf("The size of the tree is %d\n",sum);
printf("Node %d is the root node of the tree\n",a[1]);
for(int i=1;i<=sum;i++){
printf("The father of node %d is %d, the left child is %d, and the right child is %d\n",i,a[f[i]/2],a[f[i]*2],a[f[i]*2+1]);
}
return 0;
}
题意:给出一个长度为n的01串,设 s [ u ] = s [ v ] = ′ 1 ′ s[u]=s[v]='1' s[u]=s[v]=′1′,则答案需要加上 d i s ( u − v ) dis(u-v) dis(u−v),算出所有的1对对答案的贡献即可。
题解:可以乱搞,方法很多。开一个数组记录所有1的位置,记录一下所有的和sum,再用一个cnt表示当前有多少个1,每次对答案的贡献就是当前的a[i]*cnt-sum。
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int n,cnt,num;
ll sum,ans;
int q[maxn];
char a[maxn];
int main(){
scanf("%d",&n);
scanf("%s",&a);
for(int i=0;i<n;i++)
if(a[i]=='1'){
q[++cnt]=i;
sum=sum+1ll*i;
}
for(int i=cnt;i>0;i--){
sum=sum-1ll*q[i];num++;
ans=(ans+1ll*(cnt-num)*q[i]-sum)%mod;
}
printf("%lld\n",ans);
return 0;
}
题意:一个正整数,如果有k个合数因子则被称为k合因子数。m个询问,每次问1~n中有多少个k合因子数。
题解:筛法。先筛出1e5全部的素数,然后再用筛法类似的累加一下贡献即可。
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int f[maxn],ans[maxn],a[maxn];
int n,m,k;
int main(){
for(int i=2;i<=100000;i++)
if(!f[i])
for(int j=i*2;j<=100000;j+=i)f[j]++;
for(int i=2;i<=100000;i++)
if(f[i])
for(int j=i;j<=100000;j+=i)a[j]++;
// printf("%d\n",a[20]);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)ans[a[i]]++;
while(m--){
scanf("%d",&k);
printf("%d\n",ans[k]);
}
return 0;
}
题意:F题题意,但是带修。保证一定是将1改成0,0改成1。每次输出修改后的总答案。
题解:先计算出答案,对于维护我们使用线段树,维护区间1的个数和1的位置总和。若将0变1相当于增加一波贡献,若将1变0相当于减去一波贡献。假设k1和k2分别是[1,pos-1],[pos+1,n]的区间结点。对于答案就应该分别加上或减去 p o s ∗ k 1. n u m − k 1. s u m + k 2. s u m − p o s ∗ k 2. n u m pos*k1.num-k1.sum+k2.sum-pos*k2.num pos∗k1.num−k1.sum+k2.sum−pos∗k2.num
#include "stdio.h"
#include "string.h"
#include "iostream"
#include "algorithm"
#include "stack"
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
struct Node{
ll sum;
int num;
}s[maxn*4],res;
stack<int>q;
struct arr{
int pos,val;
}a[maxn];
Node cmp(Node a,Node c){
Node k;
k.num=(a.num%mod+c.num%mod)%mod;
k.sum=(a.sum%mod+c.sum%mod)%mod;
return k;
}
void pushup(int node){
s[node].sum=(s[node<<1].sum%mod+s[node<<1|1].sum%mod)%mod;
s[node].num=(s[node<<1].num%mod+s[node<<1|1].num%mod)%mod;
return;
}
void build(int l,int r,int node){
if(l==r){
s[node].num=a[l].pos;
s[node].sum=a[l].val;
return;
}
int mid=(l+r)>>1;
build(l,mid,node<<1);
build(mid+1,r,node<<1|1);
pushup(node);
}
void update(int L,int x,int l,int r,int node){
if(l==r){
if(x==1)s[node].sum=L;
if(x==0)s[node].sum=0;
s[node].num=x;
return;
}
int mid=(l+r)>>1;
if(L<=mid)update(L,x,l,mid,node<<1);
else update(L,x,mid+1,r,node<<1|1);
pushup(node);
}
Node query(int L,int R,int l,int r,int node){
if(l>=L && r<=R)return s[node];
int mid=(l+r)>>1;
Node res;res.num=0;res.sum=0;
if(L<=mid) res=cmp(res,query(L,R,l,mid,node<<1));
if(R>mid) res=cmp(res,query(L,R,mid+1,r,node<<1|1));
return res;
}
char t[maxn];
int n,m,cnt;
ll ans,sum;
int main(){
scanf("%d",&n);
scanf("%s",&t);
scanf("%d",&m);
for(int i=0;i<n;i++){
if(t[i]=='0')continue;
sum=(sum+1ll*i)%mod;
cnt++;
q.push(i);
a[i+1].pos=1;
a[i+1].val=i+1;
}
while(!q.empty()){
int k=q.top();q.pop();
sum-=k*1ll;
cnt--;
ans=(ans+1ll*cnt*k-sum)%mod;
}
build(1,n,1);
printf("%lld\n",ans);
while(m--){
int opt,pos;
scanf("%d%d",&opt,&pos);
if(opt==1){
update(pos,1,1,n,1);
Node k1,k2;
k1.sum=0ll,k1.num=0;
k2.sum=0ll,k2.num=0;
if(pos>1)k1=query(1,pos-1,1,n,1);
if(pos<n)k2=query(pos+1,n,1,n,1);
ans=(ans+1ll*pos*k1.num%mod-k1.sum%mod+k2.sum%mod-1ll*pos*k2.num%mod+2*mod)%mod;
}
else {
update(pos,0,1,n,1);
Node k1,k2;
k1.sum=0ll,k1.num=0;
k2.sum=0ll,k2.num=0;
if(pos>1) k1=query(1,pos-1,1,n,1);
if(pos<n) k2=query(pos+1,n,1,n,1);
ans=(ans-1ll*pos*k1.num%mod+k1.sum%mod-k2.sum%mod+1ll*pos*k2.num%mod+2*mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
题意:汉诺塔,问A柱到B柱,A柱到C柱,B柱到A柱,B柱到C柱,C柱到A柱,C柱到B柱,以及总共会移动多少次。
题解:拿暴力程序打一下表,可以轻松的地发现总和次数为 2 n − 1 2^n-1 2n−1,A->B和B->C满足一个规律,B->A和C->B满足一个规律,A->C和C->A分别是个规律,打表出来去OEIS上翻一翻就过了。
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
ll a,b,c,d,e,f,sum;
ll quick(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int n;
int main(){
scanf("%d",&n);
sum=quick(2,n)-1;
if(n==1)b=1;
if(n>1){
ll t=n/2-1;
a=3*t+1;
a=(a+quick(2,2*t+3))/9;
d=a;
}
if(n>2){
ll t=(n-1)/2;
for(int i=1;i<=t;i++)
c=4*f+i,f=c;
f=c;
}
if(n>3){
ll t=(n-2)/2;
e=2*(quick(4,t+1)-3*t-4)/9;
}
b=sum-a-c-d-e-f;
printf("A->B:%lld\n",a);
printf("A->C:%lld\n",b);
printf("B->A:%lld\n",c);
printf("B->C:%lld\n",d);
printf("C->A:%lld\n",e);
printf("C->B:%lld\n",f);
printf("SUM:%lld\n",sum);
return 0;
}
eg:剩下B题构造,E题数位dp,J题最短路明天补!