今天状态不好啊,净是犯小错误qwq
A.小A买彩票
一道签到题。
注意到,概率=不亏的方案数/总方案数
而,总方案数=(每次从1,2,3,4中选一个,选n次)
那么,我们只需要计算的就是不亏的方案数了。
我们注意到,n很小,而且,最多可以获得的也才元(先不考虑支付的
元)
那么,这个明显可以使用dp统计方案数。
我们设dp[i][j],表示前i次抽奖中,不考虑支付的3*i元的情况下,获得j元的方案数。
那么我们只需要分当前中的是1元,2元,3元,4元四个情况考虑转移即可。
最后,我们直接统计获得的钱数不小于3*n的方案数之和即可。
复杂度:
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[31][121];
signed main(){
int n;
scanf("%lld",&n);
dp[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=i;j<=i*4;++j){
for(int k=1;k<=4;++k){
if(j<k)break;
dp[i][j]+=dp[i-1][j-k];
}
}
}
int tot=0;
for(int i=3*n;i<=4*n;++i){
tot+=dp[n][i];
}
int sum=pow(4,n);
int D=__gcd(tot,sum);
tot/=D,sum/=D;
printf("%lld/%lld",tot,sum);
return 0;
} B.「金」点石成金
看到n这么小,而且每次的状态也就两种。。。直接爆搜即可。
需要注意的是,做减法的时候记得把差和0取最大值。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=16;
#define int long long
int a[N],b[N],c[N],d[N],ans,n;
inline int max(int x,int y){
return x>y?x:y;
}
inline void dfs(int now,int l,int r){
if(now==n+1){
ans=max(ans,l*r);
return;
}
dfs(now+1,l+c[now],max(0,r-d[now]));
dfs(now+1,max(0,l-b[now]),r+a[now]);
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i){
scanf("%lld%lld%lld%lld",&a[i],&b[i],&c[i],&d[i]);
}
dfs(1,0,0);
printf("%lld",ans);
return 0;
} C.小阳的贝壳
这是个挺套路的题目,他的第二个询问其实就已经提醒你怎么做了。
我们用一颗线段树维护一个差分数组即可。
对于区间修改,我们直接改成做差分数组的两次单点修改即可
对于询问差的最大值,我们直接找到区间最大值即可
至于询问gcd,我们先求出现在第l个数的真实的值(做个前缀和就行了),然后和l+1-r的差分数组的gcd取gcd即可。
为啥正确呢?
因为,有如下定理:
[这个大家在学辗转相除或更相减损的应该都接触过]
(这个和最值的有点相似,这个的证法很多,个人推荐用分解质因数然后转化成区间最值的形式证明)
由第二个定理,我们不难得出:
然后由第一个定理,我们不难得出:
于是,我们直接求出这个东西就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
struct node{
int w,g,s;
}t[N<<2];
int a[N];
inline int my_max(int x,int y){//绝对值最值
return abs(x)>abs(y)?x:y;
}
inline void build(int now,int l,int r){
if(l==r){
t[now].w=t[now].g=t[now].s=(a[l]);
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid),build(now<<1|1,mid+1,r);
t[now].w=my_max(t[now<<1].w,t[now<<1|1].w);
t[now].g=__gcd(t[now<<1].g,t[now<<1|1].g);
t[now].s=t[now<<1].s+t[now<<1|1].s;
}
inline void insert(int now,int l,int r,int x,int y){
if(l==r){
t[now].w=t[now].g=t[now].s=(t[now].w+y);
return;
}
int mid=(l+r)>>1;
if(x<=mid){
insert(now<<1,l,mid,x,y);
}else{
insert(now<<1|1,mid+1,r,x,y);
}
t[now].w=my_max(t[now<<1].w,t[now<<1|1].w);
t[now].g=__gcd(t[now<<1].g,t[now<<1|1].g);
t[now].s=t[now<<1].s+t[now<<1|1].s;
}
inline int find1(int now,int l,int r,int lc,int rc){//区间绝对值最值
if(lc<=l&&r<=rc){
return t[now].w;
}
int mid=(l+r)>>1,res=0;
if(lc<=mid){
res=my_max(res,find1(now<<1,l,mid,lc,rc));
}
if(rc>mid){
res=my_max(res,find1(now<<1|1,mid+1,r,lc,rc));
}
return res;
}
inline int find2(int now,int l,int r,int lc,int rc){//区间gcd
if(lc<=l&&r<=rc){
return t[now].g;
}
int mid=(l+r)>>1,res=0;
if(lc<=mid){
res=__gcd(res,find2(now<<1,l,mid,lc,rc));
}
if(rc>mid){
res=__gcd(res,find2(now<<1|1,mid+1,r,lc,rc));
}
return res;
}
inline int find3(int now,int l,int r,int lc,int rc){//区间和
if(lc<=l&&r<=rc){
return t[now].s;
}
int mid=(l+r)>>1,res=0;
if(lc<=mid){
res=(res+find3(now<<1,l,mid,lc,rc));
}
if(rc>mid){
res=(res+find3(now<<1|1,mid+1,r,lc,rc));
}
return res;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for(int i=n;i;--i){
a[i]-=a[i-1];
}
build(1,1,n);
while(m--){
int opt,l,r;
scanf("%d%d%d",&opt,&l,&r);
if(opt==1){
int x;
scanf("%d",&x);
insert(1,1,n,l,x);
if(r<n){
insert(1,1,n,r+1,-x);
}
}else if(opt==2){
if(l==r){
puts("0");
continue;
}
printf("%d\n",abs(find1(1,1,n,l+1,r)));
}else{
if(l==r){
printf("%d\n",find3(1,1,n,1,l));
}else{
printf("%d\n",abs(__gcd(find3(1,1,n,1,l),find2(1,1,n,l+1,r))));
}
}
}
return 0;
} D.Forsaken喜欢字符串
读完题,我们发现,询问其实就是求,其余的字符串中,和当前字符串中所有长度为len的子串的匹配个数之和再乘len
注意到,n,q很大,所以我们肯定不能直接比较。
但是,我们发现k非常小。
于是,我们对所有的串求出他的所有子串,然后用一个map每个字符串的出现次数。
这样,在询问的时候,我们把第x个字符串的所有长度为y的子串处理出来,然后直接查询当前子串在map的出现次数就行了。
但是,因为子串不能和同一个字符串的子串匹配,所以,我们还要算出当前子串在第x个字符串的出现次数,然后答案减去这个出现次数即可(即,只和第x个字符串以外的字符串匹配)
(思路早想到了,但实现时换了好几种写法。。。今天真的状态布星啊qwq)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+1;
int n,k;
char s[N][6];
map<string,int>sp,ep;
inline string get_string(int id,int x,int y){
string now="";
for(int i=1;i<=y;++i){
now+=s[id][x+i-1];
}
return now;
}
signed main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%s",s[i]+1);
for(int j=1;j<=k;++j){
for(int p=1;p<=k-j+1;++p){
++sp[get_string(i,p,j)];
}
}
}
int q;
scanf("%d",&q);
while(q--){
int x,y;
scanf("%d%d",&x,&y);
long long ans=0;
ep.clear();
for(int i=1;i<=k-y+1;++i){
++ep[get_string(x,i,y)];
}
for(int i=1;i<=k-y+1;++i){
string now=get_string(x,i,y);
ans+=sp[now]-ep[now];
}
printf("%lld\n",ans*y);
}
return 0;
} E.Board
见过这个题,原题不是加任意数而是加1,不过其实是一样的。。。
我们首先分析,设表示第i行加的数字之和,
表示第i列加的数字之和,第i行第j列的数字为
。
那么,对于第i行第j列的数字,它现在的值即为。
于是,我们设被隐藏的数字是第a行第b列的那个,那么它的值即为
我们想办法把这个和构造出来。
我们随便再找两个数字c,d(c,d<=n),使得c!=a,d!=b
那么,我们就有:
注意到,
而,又因为只有第a行第b列的值是不确定的,所以,这三个数字的值是确定的。
所以,我们直接输出这个式子的值就行了
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1001][1001];
signed main(){
int n,x,y;
scanf("%d",&n);
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
scanf("%d",&a[i][j]);
if(a[i][j]==-1){
x=i,y=j;
}
}
}
int k=1,t=1;
if(y==k)++k;
if(x==t)++t;
printf("%d",a[x][k]+a[t][y]-a[t][k]);
return 0;
} 
京公网安备 11010502036488号