打卡第6篇。
一套对于我来说抽象程度难以形容的题。。。。。。从第一题开始就步履维艰,第二题要调很久,第三题不难,第四题很抽象,第五题巧妙化简之后才好点,不然必爆最多5分,最后两题我不多说什么了。。。。。。还是自己太菜了有待提高。。。。
C.取模
打个表看看怎么个事。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;
while(t--){
ll n,m;cin>>n>>m;
bool flag=0;
for(ll i=1;i<m-1;i++){
for(ll j=i+1;j<=m;j++){
if((n%i)==(n%j)){
cout<<n<<"%"<<i<<" = "<<(n%i)<<" "<<n<<"%"<<j<<" = "<<(n%j)<<'\n';
}
}
}
}
return 0;
}
发现几种规律:
1. n==m时,必定有解,因为y可以等于m,式子化简为n%x==n%n,任何数模1都等于0,所以n%1==n%n,对于任意的n恒成立。
2. n<m时,除了1,2没有解,其他m-n>=2的情况下都可以有解。
3. n>m时,就不一定了。
暴力解(可以AC)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;
while(t--){
ll n,m;cin>>n>>m;
if(n==m)cout<<"Yes"<<'\n';
else{
ll flag=0;
for(ll y=2;y<=m;y++){
for(ll x=1;x<y;x++){
if(n%y==n%x){
flag=1;
break;
}
}
if(flag==1){
cout<<"Yes"<<'\n';
break;
}
}
if(flag==0)cout<<"No"<<'\n';
}
}
return 0;
}
换了个写法错了,洛谷也没过,让我看看怎么个事。。
哥们帮我打了个表。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
int m = 100000000;
for (int n = m; n >= 1; n--)
{
ll flag = 0;
for (ll x = 1; x <= m - 1; x++)
{
for (ll y = x + 1; y <= m; y++)
{
if (n % y == n % x)
{
if (x != 1)
{
cout << n << ' ';
cout << x << ' ' << y << endl;
system("pause");
}
flag = 1;
break;
}
}
if (flag == 1)
{
// cout << "Yes" << '\n';
break;
}
}
// if (flag == 0)
// cout << "No" << '\n';
}
}
6.。。。。所以y放内层很可能就超时了,不放心还可以再试试两个代码同时运行这个样例要用多少次,就一目了然了。
中国剩余定理,了解一下。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
ll t; cin >> t;
while (t--) {
ll n, m; cin >> n >> m;
if (m > 23) { // 对于 m 大于 23 的情况,可以直接输出 "Yes"
cout << "Yes" << '\n';
} else {
bool flag = false;
// 对于 m 小于等于 23,遍历所有可能的 (x, y) 组合
for (ll x = 1; x < m; x++) {
for (ll y = x + 1; y <= m; y++) {
if (n % x == n % y) {
flag = true;
break;
}
}
if (flag) break;
}
if (flag) cout << "Yes" << '\n';
else cout << "No" << '\n';
}
}
return 0;
}
D.内存空间
这个题不难,细节和坑比较多,注意读题和输入输出格式。。。
#include <bits/stdc++.h>
#define int long long//开个long long保险一点
using namespace std;
int t , ans , a[4];
string s , mp[4] = {"GB" , "MB" , "KB" , "B"};
//输入字符串和内存字符串
void solve1(){//整理数组内存
int m , num;//m为一个变量的内存,num为数组的大小
if(s[0] == 'i') m = 4;
else m = 8;
for(int i = 0; i < s.size(); i++){
if(s[i] == '['){//找中括号里面的数
num = 0;
for(int j = i + 1; s[j] != ']'; j++)
num = num * 10 + s[j] - '0';
ans += num * m;
}
}
return;
}
void solve2(){//整理变量内存
int m;//m为一个变量的内存
if(s[0] == 'i') m = 4;
else m = 8;
for(int i = 0; i < s.size(); i++)
if(s[i] == '=') ans += m;//找等于号
return;
}
void solve3(){//整理字符串内存
for(int i = 0; i < s.size(); i++){
if(s[i] == '"' && s[i - 1] == '='){
//有可能出现引号,但是前面没有等于号,不算一个字符串的开始
for(int j = i + 1; s[j] != '"'; j++)
ans++;//字符串长度
}
}
return;
}
signed main(){
cin >> t;
getchar();//输入t后再缓存区内会有换行符,所以要先getchar一下,否则会出问题
while(t--){
getline(cin , s);
if(s[0] == 'i' || s[0] == 'l'){//若为int long类型的
if((s[0] == 'i' && s[3] == ' ') || (s[0] == 'l' && s[4] == ' '))//若为变量
solve2();
else solve1();//若为数组
}
else solve3();//若为字符串
}
int cur = 4;
while(ans){
a[--cur] = ans % 1024;
ans /= 1024;
}//整理输出
for(int i = 0; i < 4; i++){
if(a[i]) cout << a[i] << mp[i];
}//输出
return 0;
}
E.斐波那契数组
暴力非全过代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+2;
ll st[N];
void init(ll st[]){
st[0]=st[1]=1;
for(ll i=2;i<N;i++){
st[i]=st[i-1]+st[i-2];
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll n;cin>>n;
vector<ll> a(n);
ll count=0;
init(st);
for(ll i=0;i<n;i++){
cin>>a[i];
if(st[i]!=a[i])count++;
}
cout<<count;
return 0;
}
注意,a0=a1,并非a0=a1=1,也有可能是2,2,4,6,10,16,…,
正确思路:
首先我们来证明一个东西:
如果 a 是一个斐波那契数组,那么满足以下关系式:
ai = fi×a1(i≥3)
其中,f 表示原始的斐波那契数列(1,1,2,3,5,8.......)。
证明如下:
设 a1=a2=x,则 a3=2x,a4=(1+2)x=3x,a5=(2+3)x=5x ······,将每个系数记到 b 里:b1=b2=1,bi=bi−1+bi−2(i≥3),与 f 的递推式是相同的。
注意到数据范围,1≤ai≤1e6,也就是说题目中的有效的斐波那契数组至多也是 30 项,我们可以输入一个很大的n,但由于ai的数据限制,所以我们可以先把斐波那契数列预处理出来,统计答案也只需要 30 个数,最后输出把后面的数加上就行了。
因为只需要满足 a0=a1 这个条件,所以我们斐波那契数组是可以在一般的斐波那契数列上乘倍数,最坏情况下是需要乘 1e6 的。
因为之前预处理了斐波那契数列,所以当前的数列与乘完倍数的斐波那契数组不同的个数的最小值,便是答案。
OK啊,问题开始变得有趣起来,让我们开始手搓代码。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll n;cin>>n;
ll a[n+1];
for(ll i=1;i<=n;i++)cin>>a[i];
ll st[31];
st[0]=st[1]=st[2]=1;
for(ll i=3;i<=30;i++)st[i]=st[i-1]+st[i-2];
//for(ll i=1;i<=30;i++)cout<<st[i]<<" ";
ll ans=LONG_MAX;
for(ll i=1;i<=N;i++){
ll res=0;
for(ll j=1;j<=min(30ll,n);j++){
res+=(a[j]!=i*st[j]);
}
ans=min(ans,res);
}
cout<<max(ans+(n-30),ans);
return 0;
}
F.近似 GCD
注意读题,子数组的数字都是连续的。
解题思路:
某个连续的子区间 ,只有在有且仅有一个数字x%g!=0的时候,才能满足条件。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// 二分查找函数,用于查找第一个大于 num 的位置
ll pos(ll l,ll r,ll num){
ll mid;
while(l<=r){
mid=(l+r)/2;
if(vis[mid]>num) // 如果 vis[mid] 大于 num,则缩小右边界
r=mid-1;
else // 否则,缩小左边界
l=mid+1;
}
return l; // 返回第一个大于 num 的位置
}
int main(){
ios::sync_with_stdio(0); // 加速输入输出
cin.tie(0); // 解绑 cin 和 cout,提高效率
cout.tie(0); // 解绑 cin 和 cout,提高效率
ll n, g;
cin >> n >> g; // 输入数组长度 n 和目标公约数 g
// vis 数组,用于记录每个位置前有多少个不满足能被 g 整除的元素
for(ll i = 1; i <= n; i++){
ll x;
cin >> x; // 输入每个元素
if(x % g) // 如果元素 x 不能被 g 整除
vis[i] = 1; // 标记 vis[i] 为 1,表示不符合条件
vis[i] += vis[i - 1]; // 累加前缀和,记录从 1 到 i 的元素中不满足条件的个数
}
ll sum = 0; // 用于存储符合条件的子数组的个数
// 遍历每个起始位置 i
for(ll i = 1; i < n; i++){
// 使用二分查找来查找符合条件的区间
sum += (pos(i + 1, n, vis[i - 1] + 1) - i - 1);
// 这里我们使用 pos 查找满足 vis[j] > vis[i-1] + 1 的第一个 j 索引
// 然后减去 i 和 1,得到符合条件的子数组个数
}
cout << sum; // 输出结果
return 0;
}
G.数组个数
提供一篇非常暴力,但非常好想的 O(nV5) 三维 DP 题解。
发现环这种情况很难搞,考虑如何去掉环的影响。
发现序列尾最多只会用到序列头的两个元素,那么枚举这两个元素,做 V2 次 DP 即可。
然后,我们设 dpi,j,k 为考虑序列的前 i 个数,最后两个数为 j,k,满足 b2 到 bi−1 限制的方案数。
转移十分简单,三重循环枚举 ai−2,ai−1,ai 这三个元素,如果满足了 bi−1 的限制那么即可转移。
最后,在 dpn,i,j 里面找答案,如果 i,j 能与我们枚举的前两个数一起,满足 b1 和 bn 的限制,那么即可统计入答案。
枚举前两个数复杂度 O(V2),单次 DP 复杂度 O(nV3),总复杂度为 O(nV5)。由于全都是简单的循环枚举,常数较小,可以通过此题。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1005,M=12,P=1e9+7;
int n,ans,b[N],dp[N][M][M];
void DP(int x,int y){
memset(dp,0,sizeof(dp));
dp[2][x][y]=1;
for(int i=3;i<=n;++i){
int lim=b[i-1];
for(int j=0;j<=lim;++j){
for(int k=0;k<=lim;++k){
for(int f=0;f<=lim;++f){
if(max(max(j,k),f)!=lim) continue;//枚举三个数,满足限制时可转移
dp[i][j][k]+=dp[i-1][f][j];
dp[i][j][k]%=P;
}
}
}
}
for(int i=0;i<=10;++i){
for(int j=0;j<=10;++j){
if(max(max(i,j),x)==b[n]&&max(max(j,x),y)==b[1])
ans=(ans+dp[n][i][j])%P;//如果满足头尾限制,更新答案
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;++i) cin>>b[i];
for(int i=0;i<=10;++i) {
for(int j=0;j<=10;++j){
DP(i,j); //枚举前两个数DP
}
}
cout<<ans;
return 0;
}
H.六六大顺
这题肯定爆,打个表找找规律。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[10];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
a[0]=0;//i=20时候就爆了
for(ll i=1;i<10;i++){
a[i]=a[i-1]*10+6;
cout<<a[i]*a[i]<<'\n';
}
return 0;
}
错误思路:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e7 + 1;
/*
S= 6*6 + 66*66 + 666*666 +...
S= ((10-1)*2/3 )*((10-1)*2/3)+
S= (10^i -1)^2 *4/9
S= 4/9 *(10^(2i) +1 -2 *10^i)
*/
//高精度加法
int main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
ll n; cin >> n;
vector<char> num1;
num1.push_back('0');
for(ll i=1;i<=n;i++){
num1.push_back('0');
num1.push_back('1');
}
//for(ll i=0;i<2*n+1;i++)cout<<num1[i];
//cout<<'\n';
vector<char> num2;
num2.push_back('0');
for(ll i=1;i<=n;i++)num2.push_back('2');
//for(ll i=0;i<n+1;i++)cout<<num2[i];
//cout<<'\n';
vector<ll> res;
ll jinwei=0;
for(ll i=0;i<n+1;i++){
ll cur=jinwei+(num1[i]-'0'+num2[i]-'0');
res.push_back(cur%10);
if(cur>=10)jinwei=cur/10;
else jinwei=0;
}
//for(ll i=0;i<n+1;i++)cout<<res[i];
//cout<<'\n';
if(jinwei>=10)jinwei=jinwei/10;
else jinwei=0;
for(ll i=n+1;i<2*n+1;i++){
ll cur=jinwei+num1[i]-'0';
res.push_back(cur%10);
if(cur>=10)jinwei=cur/10;
else jinwei=0;
}
//for(ll i=n+1;i<2*n+1;i++)cout<<res[i];
//cout<<'\n';
//for(ll i=res.size()-1;i>=0;i--)cout<<res[i];
//把n加里面再*4/9;
vector<ll> num3;
while(n){
num3.push_back(n%10);
n/=10;
}
vector<ll> ans;
jinwei=0;
for(ll i=0;i<num3.size();i++){
ll cur=jinwei+res[i];
ans.push_back(cur%10);
if(cur>=10)jinwei=cur/10;
else jinwei=0;
}
if(jinwei>=10)jinwei=jinwei/10;
else jinwei=0;
for(ll i=num3.size();i<res.size();i++){
ll cur=jinwei+res[i];
ans.push_back(cur%10);
if(cur>=10)jinwei=cur/10;
else jinwei=0;
}
//乘以4/9也是个坏事,难搞,这种思路不可行
return 0;
}
正确思路:
#include <stdio.h>
using namespace std;
const int MAX = 2e7 + 1;
int n, arr[MAX];//arr模拟高精数组
int main()
{
scanf("%d", &n);
arr[0] = 4 * n % 10, arr[1] = 4 * n / 10;//将4n存入数组
for (int i = 1; i <= n; ++i)//++i卡常小妙招(bushi
{
arr[i << 1] = 4, arr[i] -= 8;//将1010…100,22…20的4倍加给数组
arr[i + 1] += arr[i] / 10, arr[i] %= 10;
if (arr[i] < 0)
{
arr[i] += 10, --arr[i + 1];
}//进退位
}
for (int i = n + 1; arr[i] < 0; ++i)
{
arr[i + 1] += arr[i] / 10, arr[i] %= 10;
arr[i] += 10, --arr[i + 1];
}//单纯进位
for (int i = 2 * n - 1; ~i; --i)
{
arr[i] += 10 * arr[i + 1];
putchar(arr[i] / 9 + '0');
arr[i] %= 9;
}//一边输出一边除以9
return 0;
}
正确答案:
#include <bits/stdc++.h>
const int MX = 2e7 + 23;
using namespace std;
int n;
long long s[MX];
int main() {
std::cin >> n;
for (int i = 1; i <= n; ++i) {
s[1] += 36;
s[i + 1] -= 2 * 36;
s[2 * i + 1] += 36;
}
for (int i = 1; i < MX ; ++i)
s[i] += s[i - 1];
for (int i = 1; i < MX ; ++i)
s[i] += s[i - 1];
int MAX = 0;
for (int i = 1; i < MX; ++i) {
s[i + 1] += s[i] / 10;
s[i] %= 10;
if (s[i]) MAX = i;
}
for (int i = MAX; i >= 1; --i)
cout << s[i];
cout << endl;
return 0;
}
I.打折
解题思路: 记录所有物品在哪一天开始打折,在哪一天结束打折。
离散化所有时间,先暴力所有物品未打折的最小值(使用 multiset 快速找出),再枚举每一天,计算贡献,快速得出当天花费的最小值,最终即可得出答案。
代码有注释。
注意事项: 打折时间为 [s,t],意味着第 t+1 天才结束打折。
#include <bits/stdc++.h>
using namespace std;
// 2023 OneWan
const int MAXM = 100000 + 5;
int s[MAXM], t[MAXM], p[MAXM], c[MAXM]; // 对应题目输入的各数组
multiset<long long> st[MAXM]; // st[i] 为 物品 i 在所有商店的价格
vector<vector<pair<int, int>>> v(MAXM); // v[i][j] 为 商店 i 出售的 第 j 个物品
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> time; // 用于离散化 时间
for (int i = 0 ; i < m ; i++) {
cin >> s[i] >> t[i] >> p[i] >> c[i];
time.emplace_back(s[i]);
time.emplace_back(t[i] + 1); // 打折是闭区间 所以需要+1才是没有打折截止
for (int j = 0 ; j < c[i] ; j++) {
int a, b; // 物品编号及原价
cin >> a >> b;
v[i].emplace_back(a, b);
}
}
sort(time.begin(), time.end()); // 排序时间
time.resize(unique(time.begin(), time.end()) - time.begin()); // 离散化时间
auto get = [&](int t) {
return lower_bound(time.begin(), time.end(), t) - time.begin();
}; // 获取离散化后的下标
int len = time.size();
vector<vector<pair<int, int>>> startD(len), endD(len);
// startD[i] 为 第 i 天 开始打折的物品编号 和 打折后的价格
// endD[i] 为 第 i 天 结束打折的物品编号 和 打折后的价格
for (int i = 0 ; i < m ; i++) {
int starts = get(s[i]), ends = get(t[i] + 1); // 获取打折开始与结束时间离散化后的下标
for (auto& [x, y] : v[i]) {
int t = 1LL * y * p[i] / 100; // 打折后的价格
st[x].insert(y); // 把物品原价放入
startD[starts].emplace_back(x, t);
endD[ends].emplace_back(x, t);
}
}
long long temp = 0; // 用于存每天购买所有物品所用的价格
for (int i = 1 ; i <= n ; i++) temp += *st[i].begin(); // 计算不进行打折时购买所有物品所用的价格
long long ans = temp;
for (int i = 0 ; i < len ; i++) {
long long k = 0; // 打折与不打折对价格的贡献
for (auto& [x, y] : startD[i]) { // 遍历当天所有开始打折的物品 打折前价格最小值为a, 打折后价格最小值为b, 贡献为b - a
k -= *st[x].begin();
st[x].insert(y);
k += *st[x].begin();
}
for (auto& [x, y] : endD[i]) { // 遍历当天所有结束打折的物品 打折前价格最小值为a, 打折后价格最小值为b, 贡献为b - a
k -= *st[x].begin();
int t = st[x].count(y);
st[x].erase(y);
for (int j = 1 ; j < t ; j++) st[x].insert(y);
k += *st[x].begin();
}
temp += k; // 加上贡献, 由前一段转移到后一段
ans = min(ans, temp); // 找花费最小
}
cout << ans;
return 0;
}
J.替换字符
线段树.....