打卡第二篇。
A.拼正方形
#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 a=7385137888721;
ll b=10470245;
a=(ll)sqrt(a);
ll shengyu=7385137888721-a*a;
cout<<a<<" "<<"shengyu="<<shengyu<<endl;
cout<<((shengyu>=b)?"shengyu":"b")<<endl;
ll i=a+1;
b-=((2*i-1-shengyu)*4);//1个2X2=4个1X1
ll temp2=b;
while(temp2>=0){
temp2-=((2*i-1)*4);
i++;
}
cout<<i*2-1<<endl;
return 0;
}
#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 a=7385137888721;
ll b=10470245;
ll sum=b+4*a;
ll res=(ll)sqrt(sum);
cout<<res;
return 0;
}
答案是5435123,把2*2的正方形数量乘以4,相当于把一个2*2看成4个1*1,再加上1*1正方形的面积可以得出能拼出的总面积,再开二次根向下取整得到结果 。
B.劲舞团
答案:9 。
C.数字诗意
暴力枚举查找规律 ,发现奇数除了1都可以,因为任意奇数都可以拆成一个偶数+奇数的形式,题干也说了,(至少两个)。偶数除了2^n都可以。
#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 n;cin>>n;
vector<ll> a(n);
ll count=0;
for(ll i=0;i<n;i++){
cin>>a[i];
if(a[i]==1)continue;
for(ll j=1;j<=54;j++){
if(pow(2,j)==a[i]){
count++;
break;
}
}
}
cout<<count;
return 0;
}
/*
S=(i+1)*i/2;
1,2 不可以
3=1,2
4
5=2,3
6=1,2,3
7=3 4奇数都可以
8
10=1,2,3,4
12=3 4 5
14=2,3,4,5
16=
*/
D.封闭图形个数
如果cmp函数传的参数是ll &x,ll &y,代码将运行不起来 。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll tongji(ll num){
ll t=num;
ll sum=0;
while(t){
ll cur=t%10;
if(cur==0||cur==4||cur==6||cur==9)sum+=1;
else if(cur==8)sum+=2;
else sum+=0;
t/=10;
}
return sum;
}
bool cmp(pair<ll,ll> &x,pair<ll,ll> &y){
if(x.first==y.first)return true;
else{
if(x.second==y.second)return x<y;
else return x.second<y.second;
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll n;cin>>n;
vector<pair<ll,ll>> a(n,{0,0});
for(ll i=0;i<n;i++){
cin>>a[i].first;
a[i].second=tongji(a[i].first);
}
sort(a.begin(),a.end(),cmp);
for(ll i=0;i<n;i++)cout<<a[i].first<<" ";
return 0;
}
E.回文数组 (java组)
不小心多加一个题,算了不删了 ,多多益善。
这题目老演员了。
#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 n;cin>>n;
vector<ll> a(n+1,0);
for(ll i=1;i<=n;i++)cin>>a[i];
vector<ll> feet(n+2,0);//防止越界,设n+1也可以过,即使i<=n
for(ll i=1;i<=n/2;i++){///(n+1)/2也可以过
if(a[i]>a[n-i+1])feet[i]=a[i]-a[n-i+1];
//a[i]大些,a[i]要变小,a[n-i+1]不用变
else feet[n-i+1]=a[n-i+1]-a[i];
}
ll ans=0;
for(ll i=1;i<=n;i++){
ll cur=min(feet[i],feet[i+1]);
feet[i]-=cur;
feet[i+1]-=cur;
//ll danci=(feet[i]>feet[i+1])?(feet[i]-cur):(feet[i+1]-cur);
ans+=(cur+feet[i]);//当前值是feet[i],不是feet[i+1],feet[i]==0时,ans+=(cur+danci)就错了
}
cout<<ans;
return 0;
}
F.商品库存管理
超时代码:
#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 n,m;cin>>n>>m;
vector<pair<ll,ll>> op(m+1,{0,0});
for(ll i=1;i<=m;i++){
cin>>op[i].first>>op[i].second;
}
for(ll i=1;i<=m;i++){
ll st=INT_MAX;ll ed=0;
for(ll j=1;j<=m;j++){
if(j==i)continue;
st=min(st,op[j].first);
ed=max(ed,op[j].second);
}
ll ans=n-(ed-st+1);
cout<<ans<<'\n';
}
return 0;
}
正确代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 300005;
int n, m;
int d[N]; // 最终每个商品的覆盖次数
int prefix_zero[N]; // 前缀和,记录库存量为0的商品数量
int prefix_one[N]; // 前缀和,记录库存量为1的商品数量
int diff[N]; // 差分数组
int l[N], r[N]; // 每个操作的区间
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d", &l[i], &r[i]);
diff[l[i] - 1]++; // 差分数组更新
diff[r[i]]--;
}
// 计算每个商品的覆盖次数
for (int i = 1; i <= n; i++) {
d[i] = d[i - 1] + diff[i - 1];
}
// 计算前缀和
for (int i = 1; i <= n; i++) {
prefix_zero[i] = prefix_zero[i - 1] + (d[i] == 0);
prefix_one[i] = prefix_one[i - 1] + (d[i] == 1);
}
// 计算结果
for (int i = 1; i <= m; i++) {
int L = l[i], R = r[i];
int zero_count = prefix_zero[L - 1] + (prefix_one[R] - prefix_one[L - 1]) + (prefix_zero[n] - prefix_zero[R]);
printf("%d\n", zero_count);
}
return 0;
}
G.挖矿
前缀和:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e6+10;//一定要开大点不然越界
int main(){
ll n,m;cin>>n>>m;
vector<ll> a(N,0),l(N,0),r(N,0);
ll cnt=0;
for(ll i=1;i<=n;i++){
cin>>a[i];
if(a[i]>0)r[a[i]]++;
else if(a[i]<0)l[-a[i]]++;
else cnt++;
//l[i]+=l[i-1];
//r[i]+=r[i-1];
}
for(ll i=1;i<=m;i++){
l[i]+=l[i-1];
r[i]+=r[i-1];
}
/*
sort(a.begin(),a.end());
for(ll i=1;i<l.size();i++)cout<<l[i]<<" ";
cout<<endl;
for(ll i=1;i<r.size();i++)cout<<r[i]<<" ";
cout<<endl;
*/
ll feet=0;
for(ll i=1;i<=m;i++){
ll t=l[i];
if(m-2*i>0)t+=r[m-2*i];
feet=max(feet,t);
t=r[i];
if(m-2*i>0)t+=l[m-2*i];
feet=max(feet,t);
}
cout<<feet+cnt<<'\n';
return 0;
}
解题思路:
如果我们在数轴上要“同时”去左边的矿洞和右边的矿洞,往返经过原点就会消耗距离,因此不难证明,转弯 1 次和 0 次的方案才可能是最优解。因此,只有四种可能的方案:正方向走到头、反方向走到头、反方向走 t格然后往正方向走到头、正方向走 t 格然后往反方向走到头。
发现前两个方案可以与后两个方案合并。
-
统计并离散化:
- 读入所有矿洞坐标;
- 用
l[x]
记录“坐标是负数且绝对值为 xxx 的矿洞数”,用r[x]
记录“坐标是正数且数值为 xxx 的矿洞数”; - 额外用
cnt
记录“坐标为 0 的矿洞数”。
-
前缀和:
- 构造
l[i]
的前缀和,表示负坐标绝对值 ≤i 的总矿洞数; - 构造
r[i]
的前缀和,表示正坐标数值 ≤i 的总矿洞数; - 这样,只要我们能走到左边最远距离是 d,就可以直接得到挖到的负坐标矿洞数 =
l[d]
;走到右边最远距离是 d,挖到的正坐标矿洞数 =r[d]
。
- 构造
-
枚举左/右的“最远距离”
- 考虑先去左边,再去右边:如果先向左走 i 步,再回到 0,就走了 2i 步,还剩 m−2i 步可以向右。能挖到矿的数量是 l[i] + r(m−2i)(若 m−2i≥0才有效) 。
- 考虑先去右边,再去左边:若先向右走 i 步,再回到 0,也走了 2i 步,还剩 m−2i 步向左,能挖到矿的数量是 r[i] + l(m−2i)
- 我们遍历 i从 1 到 m,把这两种情况的最大值取出来。
-
加上坐标为 0 的矿洞
- 因为坐标为 0 的矿洞可以直接挖到,不用走路,所以把上面最大值再加上
cnt
即是答案。
- 因为坐标为 0 的矿洞可以直接挖到,不用走路,所以把上面最大值再加上
H.吊坠
Oh,my god,最小生成树。。。。我还不会。。以后学了来补题。
答案代码:
#include <bits/stdc++.h>
using namespace std;
/*
* 本题思路:
* 1. 输入 n, m,以及 n 个长度为 m 的环形字符串(只能旋转,不可翻转)。
* 2. 将每个长度 m 的字符串 s[i] 扩展成长 2*m 的串 arr[i] = s[i] + s[i],以便模拟“环形旋转”。
* 3. 对每对字符串 (i, j),计算它们的最长公共子串(LCSubstr)长度,记为 dist[i][j]。
* - 注意:因为是环形,arr[i]、arr[j] 均为 2*m 长,但最终的公共子串长度最多不能超过 m。
* 4. 建立一张完全图:每个字符串视为一个顶点,(i, j) 的边权 = dist[i][j]。
* 5. 在这张完全图上,用 Kruskal 算法选取 n-1 条边,使所有顶点连通且边权和最大。
* 6. 输出最大边权和。
*/
static const int MAXN = 200; // n <= 200
static const int MAXM = 50; // m <= 50
// ---------------- 并查集 (Union-Find) ---------------- //
struct UnionFind {
vector<int> parent, rank_;
UnionFind(int n) : parent(n), rank_(n, 0) {
for (int i = 0; i < n; i++) parent[i] = i;
}
int findSet(int x) {
if (parent[x] != x) {
parent[x] = findSet(parent[x]);
}
return parent[x];
}
void unionSet(int x, int y) {
int rx = findSet(x), ry = findSet(y);
if (rx != ry) {
// 按秩合并
if (rank_[rx] < rank_[ry]) swap(rx, ry);
parent[ry] = rx;
if (rank_[rx] == rank_[ry]) rank_[rx]++;
}
}
};
// ---------------- 计算两环形串的最长公共子串 ---------------- //
/*
* arr[x], arr[y] 分别是原串 s[x], s[y] 各自拼接自身后得到的长度 2*m 的字符串。
* 我们用一个 DP (Longest Common Substring):
* dp[i][j] 表示以 arr[x][i-1], arr[y][j-1] 结尾的公共子串长度。
* 时间复杂度 O((2*m)^2) = O(4*m^2)。由于 n 最多 200, m 最多 50,可以接受。
*/
int lcs(const string &sx, const string &sy, int m)
{
// sx.size()、sy.size() 都是 2*m
int len = sx.size(); // = 2*m
vector<vector<int>> dp(len + 1, vector<int>(len + 1, 0));
int maxLen = 0;
for(int i = 1; i <= len; i++){
for(int j = 1; j <= len; j++){
if(sx[i-1] == sy[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
maxLen = max(maxLen, dp[i][j]);
}
}
}
// 最长公共子串不能超过原环的实际长度 m
return min(maxLen, m);
}
// ---------------- 主函数 ---------------- //
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
// 原输入的 n 个环形字符串,长度均为 m
vector<string> s(n);
for(int i = 0; i < n; i++){
cin >> s[i];
}
/*
* 将每个字符串 s[i] 拼接自己得到 arr[i],长度 = 2*m。
* 这样 arr[i] 中任取长度 m 的连续子串都对应环形串的一种旋转。
*/
vector<string> arr(n);
for(int i = 0; i < n; i++){
arr[i] = s[i] + s[i]; // 拼接自己
}
// 预先计算每对 (i, j) 的“环形最长公共子串”长度。
// 这里 dist[i][j] = dist[j][i],图是无向的。
vector<vector<int>> dist(n, vector<int>(n, 0));
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
dist[i][j] = lcs(arr[i], arr[j], m);
dist[j][i] = dist[i][j];
}
}
// 将所有边 (i, j) 存入一个边集,边权 = dist[i][j]。
// Kruskal 需要从大到小排序(求最大生成树)。
vector<array<int,3>> edges;
edges.reserve(n*(n-1)/2);
for(int i = 0; i < n; i++){
for(int j = i+1; j < n; j++){
edges.push_back({dist[i][j], i, j});
}
}
// 按边权从大到小排序
sort(edges.begin(), edges.end(), [](auto &a, auto &b){
return a[0] > b[0]; // 降序
});
// Kruskal 算法:选 n-1 条边连通所有顶点,并使边权和最大
UnionFind uf(n);
int chosen = 0;
long long ans = 0;
for(auto &e : edges) {
int w = e[0], x = e[1], y = e[2];
if(uf.findSet(x) != uf.findSet(y)) {
uf.unionSet(x, y);
ans += w;
chosen++;
if(chosen == n - 1) break;
}
}
cout << ans << "\n";
return 0;
}
I.回文字符串
这c语言网真的服了,看看提交的时间点就知道了 。。。luogu都对了,我哥们说c语言网经常这样,见怪不怪了。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool check(string s){
ll l=0,r=s.length()-1;
while(l<=r){
if(s[l]!=s[r])return false;
l++;r--;
}
return true;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;
while(t--){
bool res=false;
string s;cin>>s;
if(check(s))res=true;
else{
ll l=0;
while(l<s.length()&&(s[l]=='l'||s[l]=='q'||s[l]=='b'))l++;
ll r=s.length()-1;
while(r>l&&(s[r]=='l'||s[r]=='q'||s[r]=='b'))r--;
string str=s.substr(l,r-l+1);
reverse(str.begin(),str.end());
if(str!=s.substr(l,r-l+1))res=false;
else{
while(l>=0&&r<s.length()&&s[l]==s[r])l--,r++;
if(l==-1)res=true;
else res=false;
}
}
if(res==false)cout<<"No"<<'\n';
else cout<<"Yes"<<'\n';
}
return 0;
}
结束!