T1:有趣的数
小强作为OI圈爸爸级别的人物,有一天随手AK的ZROI的J转S模拟赛。 AK完了的小强闲得无聊,于是随手在纸上面写了几个看上去非常有规律的数字,555655,24444,7787,110。他觉得这些数字非常的有趣,并开心地拍起了手。 于是小强定义一个有趣的数为:对于一个正整数,对于其所有的数位,当且仅当它恰好有一位与其他位不同,则称这个数为有趣的数。 比如:555655,24444,7787,110都为有趣的数,而 6996,4444 都不是。 小强想要知道在[L,R]中有多少个有趣的数.
Solution
60分做法: 暴力区间扫一遍
100分解法 我们发现就算整个范围有
1
0
16
10^{16}
1016,总的数的个数也不会很多,所以我们可以尝试枚举每一个可能的数然后在判断是否在给定的范围内
一些细节处理需要注意
Code
#include<bits/stdc++.h>
using namespace std
;
#define int long long
int l
,r
;
bool check(int le
,int s
,int d
,int p
){
if (d
== 0 && p
== 1) return 0;
if (s
== 0 && p
!=1) return 0;
int sum
= 0;
for (int i
=1;i
<=le
;i
++)
if (i
== p
) sum
= sum
*10+d
;
else sum
= sum
*10+s
;
return l
<=sum
&& sum
<=r
;
}
signed main(){
freopen("interestnum.in","r",stdin);
freopen("interestnum.out","w",stdout);
int ans
= 0;
scanf("%lld %lld",&l
,&r
);
for (int len
=3; len
<=17;len
++)
for (int Sa
=0;Sa
<=9;Sa
++)
for (int Di
=0;Di
<=9;Di
++){
if (Sa
== Di
) continue;
for (int ps
=1;ps
<=len
;ps
++)
if (check(len
,Sa
,Di
,ps
)) ans
++;
}
printf("%lld",ans
);
}
T2:欢乐ABC
80分做法:暴力扫一遍在判断100分做法: 先用前缀和来求出A、B、C字符的个数,然后一波乱推在经过移项可以得到当
A
[
i
−
1
]
−
B
[
i
−
1
]
=
=
A
[
j
]
−
B
[
j
]
且
B
[
i
−
1
]
−
C
[
i
−
1
]
=
=
B
[
j
]
−
C
[
j
]
A[i-1]- B[i-1] ==A[j]-B[j] 且 B[i-1]-C[i-1] == B[j] -C[j]
A[i−1]−B[i−1]==A[j]−B[j]且B[i−1]−C[i−1]==B[j]−C[j]时满足一对
我们开个map记录这个二元组即可
Code
#include<bits/stdc++.h>
using namespace std
;
#define mp make_pair
map
< pair
< int , int > , int > M
;
string s
;
int suma
[1010100],sumb
[1010100],sumc
[1010100];
int main(){
freopen("happyabc.in","r",stdin);
freopen("happyabc.out","w",stdout);
long long ans
= 0;
cin
>>s
;M
[mp(0,0)] = 1;
for (int i
=0;i
<s
.size();i
++)
suma
[i
+1]+= (s
[i
]=='A') + suma
[i
],
sumb
[i
+1]+= (s
[i
]=='B') + sumb
[i
],
sumc
[i
+1]+= (s
[i
]=='C') + sumc
[i
];
int n
= s
.size();
for (int i
=1;i
<=n
;i
++){
int A
= suma
[i
]-sumb
[i
] , B
= sumb
[i
] - sumc
[i
];
ans
+=(long long)M
[mp(A
,B
)]++;
}
printf("%lld",ans
);
return 0;
}
T3:数位问题
30-60做法:dfs100分做法 设
f
[
i
]
[
j
]
[
k
]
表
示
用
了
前
i
个
数
,
膜
11
的
余
数
为
j
,
有
k
个
数
在
奇
数
上
的
数
是
否
能
被
11
整
除
f[i][j][k]表示用了前i个数,膜11的余数为j,有k个数在奇数上的数是否能被11整除
f[i][j][k]表示用了前i个数,膜11的余数为j,有k个数在奇数上的数是否能被11整除 我们可以得到以下很显然的方程:
f
[
i
]
[
j
]
[
k
]
∣
=
f
[
i
−
1
]
[
(
j
−
a
[
i
]
+
11
)
%
11
]
[
k
−
1
]
∣
∣
f
[
i
−
1
]
[
(
j
+
a
[
i
]
)
%
11
]
[
k
]
f[i][j][k]|=f[i-1][(j-a[i]+11)\%11][k-1]\ ||\ f[i-1][(j+a[i])\%11][k]
f[i][j][k]∣=f[i−1][(j−a[i]+11)%11][k−1] ∣∣ f[i−1][(j+a[i])%11][k]意思是分别将当前数放到奇数位和偶数位是否可行转化成答案时需要注意以下方法
Code
#include<bits/stdc++.h>
using namespace std
;
int t
;
bool f
[201][20][501];
int a
[10110000];
void work(){
int len
= 0;
for (int i
=1;i
<=9;i
++){
int x
;
scanf("%d",&x
);
for (int j
=1;j
<=x
;j
++)
a
[++len
] = i
;
}
memset(f
,0,sizeof(f
));
f
[0][0][0] = 1;
for (int i
=1;i
<=len
;i
++)
for (int j
=0;j
<11;j
++)
for (int k
=0;k
<=i
;k
++)
f
[i
][j
][k
]|=f
[i
-1][(j
-a
[i
]+11)%11][k
-1] | f
[i
-1][(j
+a
[i
])%11][k
];
int ans
= 10000000000000;
for (int i
=0;i
<=len
;i
++)
if (f
[len
][0][i
])
if (i
*2 == len
) ans
= min(ans
,len
);
else ans
= min(ans
,max(i
,len
-i
+1)*2-1);
printf("%d\n",ans
>2*len
-1?-1:ans
);
}
main(){
freopen("digit.in","r",stdin);
freopen("digit.out","w",stdout);
scanf("%d",&t
);
while (t
--) work();
return 0;
}
T4:打游戏
30分做法: 将m=0的直接搞掉再将m=1的特判搞掉 如果当前位置在第k个位置,总共有n个怪,第k个怪的血量为
a
[
k
]
a[k]
a[k],那么打掉第k个怪物的代价就是
(
n
−
k
+
1
)
∗
(
a
[
k
]
−
1
)
+
n
−
k
(n-k+1)*(a[k]-1)+n-k
(n−k+1)∗(a[k]−1)+n−k(手推即可)100分做法: 贪心,如果总的怪数>3或者有一滴血的怪,那么就用群体攻击(前提是有魔法) 如果怪的总是小于3,那么就用重击 如果没有魔法,那么就普通攻击(按照上述所说的方法进行计算)
Code
#include<bits/stdc++.h>
using namespace std
;
#define int long long
int n
,m
,ans
= 0;
int h
[1010100];
signed main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%lld %lld",&n
,&m
);
for (int i
=1;i
<=n
;i
++) scanf("%lld",&h
[i
]);
sort(h
+1,h
+n
+1);
int k
= 1;
while (k
<=n
){
if ((k
+2<=n
||h
[k
]==1) && m
){
for (int i
=k
;i
<=n
;i
++) h
[i
]--;
while (h
[k
] == 0 && k
<=n
) k
++;
ans
+=n
-k
+1;
m
--;
continue;
}
if (h
[k
] == 1 && m
&& k
<=n
) {h
[k
++] = 0,ans
+=n
-k
+1;continue;}
else if (m
){
h
[k
]-=2;
if (h
[k
]<=0) k
++;
ans
+=n
-k
+1;
m
--;
continue;
}
break;
}
for (int i
=k
;i
<=n
;i
++)
ans
+=(n
-i
+1)*(h
[i
]-1)+n
-i
;
printf("%lld",ans
);
}
总结:一般数位dp的题目一般都可以用dfs暴力写,把该拿的部分分都拿到。如果一道题是在没什么思路,不放以大局观的暴力解一道题目,说不定更优