寒假两周训练总结

1.codeforces
2.牛客寒假训练营(一)
3.牛客寒假训练营(二)
4.牛客寒假训练营(三)
5.牛客寒假训练营(四)
总的情况打的情况不好,在家有干扰还有事情较多,希望能更加保持专注,下一周更加进步有收获
这一周的问题发现的也不少,很多知识有些忘记了,还有对题目的分析和回忆学过的知识,细想起来真的很多,一点一点补回来
1.

A. Level Statistics
简单的模拟,细心一点,看清题目问的是什么,每一次是和上一次比较

点击查看代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        
        bool pd = true;
        int lp = 0, lc = 0;
        for(int i = 0; i < n; ++i){
            int p, c;
            cin >> p >> c;
            if(p < lp || c < lc){
                pd = false;
            }
            if(p < c){
                pd = false;
            }
            int dp = p - lp;
            int dc = c - lc;
            if(dp < dc){
                pd = false;
            }
            lp = p;
            lc = c;
        }
        if(pd){
            cout << "YES\n";
        }
        else{
            cout << "NO\n";
        }
    }
    
    return 0;
}

B. Middle Class
贪心,从最大开始取,直到不满足条件退出循环

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int v[1008611];

int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t;
    cin >> t;
    while(t--){
        int n,k;
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>v[i];
        }
        sort(v+1,v+1+n,greater<int>());
        int sum=0;
        int res=0;
        for(int i=1;i<=n;i++){
            sum+=v[i];
            if((1.00*sum/i)<k*1.00){
                 break;
            }else{
                res++;
            }
        }
        cout<<res<<'\n';
    }
    
    return 0;
}

C. Circle of Monsters
因为 b 都是大于 0 的数,所以最优解肯定是先挑选一个敌人开始,然后按照顺序依次击杀,不难看出这样是最优的,那么找出第一个击杀的敌人成了这个题的突破口,我的第一反应是找到 a[ i ] 的最小值入手,但不幸的是这样做并不对,后来看到了数据范围给了提示,就恍然大悟了,想一下为什么 a 和 b 的数值都给到了 1e12 ,而不是正常的 1e9 或 1e5 呢,显然是需要进行某种操作,而不能超过 1e18 ,这相差了不到 1e6 的量级恰好就和敌人的数量 n 对应了起来,所以数据范围提示我们需要维护一个前缀和,这样一想我们因为只是第一个敌人的选择不一样,所以可以一层循环枚举起点然后维护最小值作为答案

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int v[1008611];
int d[1008611];
int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t;
    cin >> t;
    while(t--){
       int n;
       cin>>n;
       int minn=1e12;
       for(int i=0;i<n;i++){
        cin>>v[i]>>d[i];
        }
        int ans=0;
        for(int i=0;i<n;i++){
            ans+=v[i];
			ans-=min(v[(i+1)%n],d[i]);
			minn=min(minn,min(v[(i+1)%n],d[i]));
        }
       cout<<ans+minn<<'\n';
    }
    
    return 0;
}

D. Minimum Euler Cycle

茕茕孑立之影
找一个大于1e9的素数即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;

#define int long long
int v[1008611];

int32_t main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int sum=1;
        int pd=0;
        for(int i=1;i<=n;i++){
            cin>>v[i];
            if(v[i]==1){
                pd=1;
            }
        }
        if(pd){
            cout<<-1<<'\n';
        }else
        cout<<5201314789<<'\n';
    }
}

一气贯通之刃
判断是否存在只有两个节点是1度,其余节点都是2度

点击查看代码
#include <bits/stdc++.h>
using namespace std;

#define int long long
int v[1008611];

int32_t main(){
  int n;
  cin >> n;

  if (n == 1) {
    cout << -1 << '\n';
    return 0;
  }

  vector<tuple<int, int>> edges;
  vector<vector<int>> adj(n);
  vector<int> degree(n, 0);

  for (int i = 0; i < n - 1; ++i) {
    int u, v;
    cin >> u >> v;
    --u; --v;
    edges.emplace_back(u, v);
    adj[u].push_back(v);
    adj[v].push_back(u);
    degree[u]++;
    degree[v]++;
  }

  int snode = -1;
  int enode = -1;
  int cdo = 0;
  for (int i = 0; i < n; ++i) {
    if (degree[i] == 1) {
      cdo++;
      if (snode == -1) snode = i;
      else enode = i;
    }
  }

  if (cdo != 2) {
    cout << -1 << '\n';
  } else {
    cout << snode + 1 << " " << enode + 1 << '\n';
  }

  return 0;
}

双生双宿之决

点击查看代码
#include <bits/stdc++.h>
using namespace std;

#define int long long
int v[1008611];
const int N=1e8+5;
bool vis[N];
int prime[6000000];
int cnt;
 


int32_t main(){
     
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        set<int>st;
        int a,b;
        map<int,int>mp;
        for(int i=1;i<=n;i++){
            cin>>v[i];
            if(i==1){
                a=v[i];
            }else{
                if(v[i]!=a){
                    b=v[i];
                }
            }
            mp[v[i]]++;
           st.insert(v[i]);
        }
       if(n%2==0&&st.size()==2&&mp[a]==mp[b]){
           cout<<"Yes\n";
       }else{
           cout<<"No\n";
       }
        
    }
}

井然有序之衡
贪心,我们可以先判断是否满足条件,利用等差数列的求和公式判断,然后把数组从小到大排列,看每个值与i差多少,是否满足即可

点击查看代码
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    long long n;
    cin >> n;
    
    vector<long long> a(n);
    for(auto &x: a){
        cin >> x;
    }
    
    long long res = n * (n +1) /2;
    long long sq = accumulate(a.begin(), a.end(), 0LL);
    
    if(sq != res){
        cout << "-1\n";
        return 0;
    }
    
    sort(a.begin(), a.end());
    
    long long ans =0;
    for(long long i=0;i<n;i++){
        ans += abs(a[i] - (i+1));
    }
    
   
    cout << (ans /2) << "\n";
    
    return 0;
}

井然有序之窗
我们可以使用优先队列维护右端点最小的区间,对区间按左端点排序后,可以从前往后将左端点小于i 的区间加入优先队列,然后取出右端点最小的区间。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
    struct node {
        int l;
        int r;
        int idx;
    };
    
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    
    vector<node> v(n);
    for(int i=0; i<n; ++i){
        cin >> v[i].l >> v[i].r;
        v[i].idx = i;
    }
    
    sort(v.begin(), v.end(), [&](const node &a, const node &b) -> bool{
        if(a.l != b.l) return a.l < b.l;
        return a.r < b.r;
    });
    
    priority_queue<pair<int, int>, vector<pair<int, int>>,greater<pair<int, int>>> pq;
    
    vector<int> result(n, -1);
    int pos = 0;
    
    for(int i=1; i<=n; ++i){
        while(pos < n && v[pos].l <= i){
            pq.emplace(v[pos].r, v[pos].idx);
            pos++;
        }
        while(!pq.empty() && pq.top().first < i){
            pq.pop();
        }
        
        if(pq.empty()){
            cout << "-1";
            return 0;
        }
        
        auto [r, idx] = pq.top();
        pq.pop();
        result[idx] = i;
    }
    
  
    for(int i=0; i<n; ++i){
        if(result[i] == -1){
            cout << "-1";
            return 0;
        }
    }
    
    for(int i=0; i<n; ++i){
        cout << result[i] << (i!=n-1 ? ' ' : '\n');
    }
    
    return 0;
}

3. 这一场牛客打感觉区分太明显,不会做的题就是做不出来,我补的我不会的题

智乃的Notepad(Easy version)
我的思路差不多,但是只过了四分之一,后面可能状态思路有问题,想歪了,找公共前缀然后数学推导
全部字符串的长度和-排序后相邻字符串的lcp)*2-最长字符串长度。,也可以用字典树

#include<bits/stdc++.h>
using namespace std;

const int M=1e5+10;
string a[M];
int n,m;

int lcp(const string &A,const string &B)
{
	int i=0;
	while(i<A.size()&&i<B.size()&&A[i]==B[i]) i++;
	return i;
}

int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n);
	int mx=0,sum=0;
	for(int i=0;i<n;i++)
	{
		sum+=(int)a[i].size()*2;
		if(i) sum-=lcp(a[i],a[i-1])*2;
		mx=max(mx,(int)a[i].size());
	}
	
	cout<<sum-mx;
	
	return 0;
}

智乃与数模

可以使用一种叫做整除分块/数论分块的技巧暴力枚举

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main()
{
    int N, K;
    cin >> N >> K;
    int L = 1, R = N;
    int val = 0, vtot = 0;
    while (L <= R)
    {
        int mid = (L + R) / 2;
        int tot = 0;
        for (int l = 1, r; l <= N; l = r + 1)
        {
            r = N / (N / l);
            int a = N - N / l * l;
            int k = (N / l);
            if (a < mid)continue;
            //cout << "a " << a << " " << mid << endl;
            tot += min((a - mid) / k + 1, r - l + 1);
        }
        //cout << "aaa " << mid << " " << tot << endl;
        if (tot >= K)
        {
            L = mid + 1;
        } else
        {
            vtot = tot;
            val = mid;
            R = mid - 1;
        }
    }
    //cout << val << " ** " << vtot << endl;
    ll ans = 1LL * (K - vtot) * (val - 1);
    for (int l = 1, r; l <= N; l = r + 1)
    {
        r = N / (N / l);
        int a = N - N / l * l;
        int k = (N / l);
        if (a < val)continue;
        int len = min((a - val) / k + 1, r - l + 1);
        //cout << "calc " << a << " " << k << " " << len << endl;
        ans += 1LL * (a * 2 - k * (len - 1)) * len / 2;
    }
    printf("%lld\n", ans);
    return 0;
}

D Tokitsukaze
字符串、贪心、分类讨论
分析题目根据C有下面结论
首尾相同的字符串一定是平衡的。
首尾不相同的字符串一定是不平衡的。
一个不平衡的字符串只有翻转首尾字符才可能变成平衡的。
一个平衡的字符串翻转除了首尾外的任意一个字符后依然是平衡的。

由于字符串可以重排,那么回文串就相当于是一种连连看,消除相同的字符。

如果一个字符在短字符串中出现了,在长的字符串中也出现了,那就将这个字符在两个字符串中各删除一个。

如果一个字符在短字符串中出现了,在长的字符串中没出现,那这个字符就无法消除,只能进行修改, 例如短的字符串为 "abc" ,长的字符串为 "abddf" ,我们可以重排后连起来变成 "abc | ddfba" ,实际上等效于 "c" 和 "ddf" 拼接成 "c | ddf" ,。 在用短的字符串尝试消除长的字符串后,长的字符串就残余了一些字母,这些残余的字母两两之间也可以相互抵消,最后字母个数为奇数的都会残余一个无法抵消的,记录为ans.
#include<bits/stdc++.h>

using namespace std;

const int M = 1e9 + 7;

int main(){
    int T;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        string s;
        cin >> s;
        if(s.size() == 1){
            if(s == "?") cout << 2 << endl;
            else cout << 1 << endl;
            continue;
        }
        s = " " + s;
        int cnt = ranges::count(s, '?');
        int ksm = 1;
        for(int i = 1; i <= cnt; i++){
            ksm *= 2;
            ksm %= M;
        }
        int ksm_1 = 1;
        for(int i = 1; i <= cnt - 1; i++){
            ksm_1 *= 2;
            ksm_1 %= M;
        }
        int ans = 0;
        if(s[1] != '?' && s[n] != '?'){
            if(s[1] == s[n]) ans = 1ll * ksm * (n - 2) % M;
            else ans = ksm * 2 % M;
        }
        else ans = 1ll * ksm_1 * n % M;
        cout << ans << endl;
    }
}

posted @ 2025-02-07 19:03  冬天的睡袋  阅读(17)  评论(0)    收藏  举报