从0开始的复盘-----2(25寒假营版1)

额,这篇写的估计很难受,我现在是在这场后三个月来写的复盘,但是我好像很难再补一题,其实知识点没那么难,只能大概的学一下思路,还是太弱了

这场的顺序就按兰子的难度排序向下讲吧(虽然别人已经有了题解,但是我还是站在弱手的角度再进行一些比较粗浅的理解和思路)

难度方面(同档次难度升序):

easy:A D B G

mid:E M J H

hard:C F L

very hard: K I

https://ac.nowcoder.com/acm/contest/95323/A

看起来就是让你找质数,但是其实质数也有倍数,所以我们直接找范围内最大的质数就行,然后虽然题目说的是1e18但是输入条件就写到1e9,所以我们直接选择1e9+7就行,同时注意一下如果出现1直接输出-1就行(小坑)

void solve() {
	ll n;
	cin >> n;
	vector<ll>a(n);
	bool ok = false;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		if(a[i]==1)ok = true;
	}
	if(ok)cout<<-1<<endl;
	else cout<<1000000007<<endl;
}

https://ac.nowcoder.com/acm/contest/95323/D
这题也是一眼啊,计数题,直接用map记录各个数字出现的次数就行(可以记录下来排序比较最大和最小的数出现的次数也行

void solve() {
	map<ll,ll>m;
	ll n,x;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>x;
		m[x]++;
	}
	auto l=*max_element(m.begin(),m.end());
	auto r=*min_element(m.begin(),m.end());
	if(m.size()!=2 || l.second!= r.second){
		cout<<"No"<<endl;
	}else 
	cout<<"Yes"<<endl;
}

or

void solve() {
	map<ll,ll>m;
	ll n;
	cin>>n;
	vector<ll>a(n);
	for(int i=0;i<n;i++){
		cin>>a[i];
		m[a[i]]++;
	}
	sort(a.begin(),a.end());
	if(m.size()!=2 || m[a[0]]!= m[a[n-1]]){
		cout<<"No"<<endl;
	}else 
	cout<<"Yes"<<endl;
}

这好像只是实现 的不同(思考)赶紧下一题吧;

https://ac.nowcoder.com/acm/contest/95323/B

这题看起来有点难写如果是建树的话,但是其实是一个另类的思维算是误导题,如果学了一点的人可能会直接建树然后从一个点开始搜到底,然后再从底搜到头,复杂度好像可行,但是当时我什么都不会,就没有踩这个坑,直接把所有输入的数字用map记录然后算谁只出现一次就行

void solve(){
  ll n;
  cin>>n;
  ll a[(n-1)*2],b[n+1]={0};
  for(int i=0;i<(n-1)*2;i++){cin>>a[i];b[a[i]]++;}
  ll q=0,w;
  for(int i=1;i<=n;i++){
    if(b[i]==1){if(q==0)q=i;else w=i;}
    if(b[i]!=2 && b[i]!=1){
      cout<<"-1"<<endl;return;
    }
  }
  cout<<q<<" "<<w<<endl;
}

这段是我当时写的,看着差不多就直接拿过来用了,虽然细节上其实不够优美


https://ac.nowcoder.com/acm/contest/95323/G

我本来以为这题可以直接算,结果重新写了一遍发现写错了,然后看了一下我以前的代码不得不说,很巧妙,当时也没有想到数学但是有更准确的写法然后脑子也比较好用直接排序检查出答案,看起来当时可能比我现在还强(我刚刚用map写了一下太shi了还错了)

void solve() {
	ll n, sum = 0, c=0;
	cin >> n;
	vector<ll>a(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		sum += a[i] - (i + 1);
	}
	if (sum != 0) {
		cout << "-1" << endl;
		return;
	}
	sort(a.begin(),a.end());
	for (int i = n - 1; i > 0; i--) {
		int e = a[i] - (i + 1);
		if (e < 0)break;
		else {
			c += e;
		}
	}
	cout << c << endl;
}

接下来就是中等题了:

https://ac.nowcoder.com/acm/contest/95323/E

这题是结合了G和D,需要找中位数,这题我是赛后补的,写起来还是有待提高啊,这题的思路就是先排序分出2堆代表双生数,然后直接找到分别的中位数,然后可能会有1的偏差(调整整个数组到这个数 的abs)最后用min选一下最小值就行了

直接借用佬的代码了

int n;
        cin >> n;
        vector<int> a(n);
        for(int i = 0; i < n; i++){
            cin >> a[i];
        }
        sort(a.begin(), a.end());
        LL ans = 1e18;
        vector<int> b(a.begin(), a.begin() + n / 2);
        vector<int> c(a.begin() + n / 2, a.end());
        auto get_mid = [&](vector<int> &a){
            return a[(a.size() - 1) / 2];
        };
        auto solve = [&](vector<int> &v, int target){
            LL sum = 0;
            for(auto x : v) sum += abs(x - target);
            return sum;
        };
        int m1 = get_mid(b), m2 = get_mid(c);
        for(int i = -1; i <= 1; i++){
            for(int j = -1; j <= 1; j++){
                if (m1 + i < m2 + j){
                    ans = min(ans, solve(b, m1 + i) + solve(c, m2 + j));
                }
            }
        }
        cout << ans << '\n';

很明显感觉到其实我现在还不如三个月前?思维,精力,毅力。感觉丢掉了好多东西,想法太多了现在,差距可能是那段时间我已经规划好了,猛猛做就行,现在看到的又多了,太贪心了注意力分散,就减少了很多激情和热爱,调整一下要改了;

https://ac.nowcoder.com/acm/contest/95323/M

这道题,我印象很深,我想了很久都错,刚刚又看一次题目还在想如果是要最大怎么写,可能就要判断区间差值了感觉复杂度O^{3}啊,但是这题是判断最小,那肯定就是不能把最大的数*2,然后要把和最小数差值*2小于和最大的差值的数*2,所以好像直接定位最小的数左右搜就行

void solve() {
	ll n;
	cin>>n;
	vector<ll>a(n);
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	ll minn=min_element(a.begin(),a.end())-a.begin();
	ll maxx=*max_element(a.begin(),a.end());
	for(int i=minn;i>=0;i--){
		if(2*a[i]<=maxx){
			a[i]*=2;
		}else break;
	}
	for(int i=minn+1;i<n;i++){
		if(2*a[i]<=maxx){
			a[i]*=2;
		}else break;
	}
	cout<<*max_element(a.begin(),a.end())-*min_element(a.begin(),a.end())<<endl;
}

但是要判断多个最小值和并非最小结果,可以通过排序来从小到大的试最小数集合是否会变得更大,核心就是不动最大的数(直接上官解了)

pair<int,int>a[202020];
int b[202020];
signed main(){
    int n,i;
    cin>>n;
    for(i=0;i<n;i++){
        cin>>a[i].first,a[i].second=i;
        b[i]=a[i].first;
    }
    a[n].first=2e9;
    sort(a,a+n);
    int l=a[0].second,r=a[0].second;
    int ma=max(a[0].first*2,a[n-1].first);
    int res=ma-min(a[0].first*2,a[1].first);
    for(i=1;i<n;i++){
        while(a[i].second<l){
            l--;
            ma=max(ma,b[l]*2);
        }
        while(a[i].second>r){
            r++;
            ma=max(ma,b[r]*2);
        }
        res=min(res,ma-min(a[0].first*2,a[i+1].first));
    }
    cout<<res;
}

但是我感觉这一篇可能更好一点

 cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> a[i];

    iota(id + 1, id + n + 1, 1);
    sort(id + 1, id + n + 1, [](int x, int y) {return a[x] < a[y];});
    int ans = 2 * (a[id[n]] - a[id[1]]), cnt = 0;
    for(int i = 1; i < n; i ++) {
        int u = id[i];
        vis[u] = 1;
        cnt ++;
        if(vis[u - 1]) cnt --;
        if(vis[u + 1]) cnt --;
        if(cnt <= 1) ans = min(ans, max(2 * a[u], a[id[n]]) - min(a[id[i + 1]], 2 * a[id[1]]));
    }
    cout << ans << "\n";

https://ac.nowcoder.com/acm/contest/95323/J

这一题很多人一眼看到肯定是简单的暴力吧,但是仔细看数据范围会发现肯定要用一定的优化

如果是单纯的暴力

void solve(){
	ll n;
	cin>>n;
	vector<ll>a(n);
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	ll c=0;
	for(int i=0;i<n;i++){
		for(int j=i+1;j<n;j++){
			if((ll)gcd(a[i],a[j])==(a[i]^a[j]))c++;
		}
	}
	cout<<c <<endl;
}

这样出来肯定是会超时的,然后我们可以用一点数学来优化一下,先把各个数存起来,然后让j每次加上i达到降低世界复杂度但是依旧遍历每个数的需求

ll t[N];
void sov(){
    int i,j,x,y;
    ll ans = 0;
    cin >> n;
    for(i = 1;i <= n;i++){
        cin >> x;
        t[x]++;
    }
    for(i = 1;i <= M;i++) for(j = i;j <= M;j += i){
        if(__gcd(j,j ^ i) == i) ans += t[j] * t[j ^ i];
    }
    cout << ans / 2 << "\n";
}

这可以学习一下,一个非常好的优化思想

https://ac.nowcoder.com/acm/contest/95323/H

这一题是很经典的选择问题,比如搬桌子啊,还有时间分配啊什么的找最优解的数独啊之类的,这道题主要是用优先队列加上pair,主要是同一时间最早结束的一段优先选择,如果无法选择就-1(一道很好的典)

struct node{
    int l,r,x;
}a[N];
int Ans[N];
bool cmp(const node &x,const node &y){
    return x.l<y.l;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>pq;
void solve(){
    ll n;
    cin>>n;
    for (int i=1; i<=n; ++i) cin>>a[i].l>>a[i].r,a[i].x=i;
    sort(a+1,a+n+1,cmp);
    int l=1;
    for (int i=1; i<=n; ++i){
        while (l<=n && a[l].l==i) pq.push({a[l].r,a[l].x}),l++;
        if (!pq.empty()){
            auto [x,y]=pq.top();
            pq.pop();
            Ans[y]=i;
            if (x<i){
                cout<<-1;
                return;
            }
        } else{
            cout<<-1;
            return;
        }
    }
    for (int i=1; i<=n; ++i) cout<<Ans[i]<<" \n"[i==n];
}

https://ac.nowcoder.com/acm/contest/95323/C

这题是一个很麻烦的大大大模拟,但是说是有一个比较爽的思路就是先塞到里面,就近找就行了,这题主要是看着就不想写吧,只能说肯写还是能打的,我选择直接copy(这场rk1大佬的码)

 int n;
        cin >> n;
        vector<string> g(n);
        for(int i = 0; i < n; i++){
            cin >> g[i];
        }
        vector<vector<bool> > v(n, vector<bool>(n));
        vector<array<int, 4> > ans;
 
        auto solve = [&](int sx, int sy){
            vector<vector<int> > d(n, vector<int>(n, -1));
            vector<vector<pair<int, int> > > pre(n, vector<pair<int, int> >(n));
            d[sx][sy] = 0;
            queue<pair<int, int> > q;
            q.push({sx, sy});
            while(!q.empty()){
                auto [x, y] = q.front();
                q.pop();
                if (g[x][y] == '1'){
                    while(x != sx or y != sy){
                        auto [nx, ny] = pre[x][y];
                        swap(g[x][y], g[nx][ny]);
                        ans.push_back({x, y, nx, ny});
                        x = nx, y = ny;
                    }
                    return;
                }
                for(int u = 0; u < 4; u++){
                    int nx = x + dx[u];
                    int ny = y + dy[u];
                    if (nx < 0 or nx >= n or ny < 0 or ny >= n) continue;
                    if (v[nx][ny] or d[nx][ny] != -1) continue;
                    d[nx][ny] = d[x][y] + 1;
                    pre[nx][ny] = {x, y};
                    q.push({nx, ny});
                }
            }
        };
 
        for(int i = 0; i < n / 2; i++){
            for(int j = 0; j < n / 2; j++){
                if (g[i][j] == '0'){
                    solve(i, j);
                }
                v[i][j] = true;
            }
        }
        // for(auto &s: g) cout << s << '\n';
        cout << ans.size() << '\n';
        for(auto [a, b, c, d] : ans){
            cout << a + 1 << ' ' << b + 1 << ' ' << c + 1 << ' ' << d + 1 << '\n';
        }
    }

https://ac.nowcoder.com/acm/contest/95323/F

这是我目前能补起来的最后一道题了,再往下只能讲讲大概,但是赛时基本上没希望,等我吧我所有的题基本上复盘完了我就来吧这些题开满

这题还是我们的双生数组,最好看的代码还是我们—cup-cpp大佬的,这题主要是要敢,敢暴力,其实大家看这题都能看出来思路,但是都觉得没这么暴力吧,过得有点太少了,我写的太shi了所有我选择直接头佬的代码

 int n;
        cin >> n;
        vector<int> a(n);
        for(int i = 0; i < n; i++) cin >> a[i];
        LL ans = 0;
        for(int i = 0; i < n; i++){
            int j = i;
            set<int> s;
            s.insert(a[i]);
 
            auto check = [&](int x){
                if (s.size() <= 1) return true;
                return s.contains(x);
            };
 
            while(j + 1 < n and check(a[j + 1])){
                s.insert(a[++j]);
            }
            if (s.size() == 1){
                break;
            }
            map<int, int> mp;
            mp[0] = 1;
            int sum = 0;
            for(int k = i; k <= j; k++){
                sum += (a[k] == a[i] ? 1 : -1);
                ans += mp[sum];
                mp[sum] += 1;
            }
            int t = j;
            while(t - 1 >= i and a[t - 1] == a[j]) t--;
            i = t - 1;
        }
        cout << ans << '\n';
    }

主要就是一个个去输入这个数组,用双指针一遍遍去找,我感觉可以开一个二维数组直接存一下各个数的多少,直接o1查找,std是用前缀和查0,比较常用和常忘的优化方法(对我而言)当然我赛后一小会这题我就a出来了(纯暴力)当时就这题让我感觉很有成就感啊

void solve(){
    ll n,ct=0;
    cin>>n;
    vector<int>a(n+2);
    for(int i=0;i<n;i++)cin>>a[i];
    a[n]=1e9+1;
    a[n+1]=1e9+2;
    map<int,int>m1;
    vector<int>v;
    set<int>m;
    int l=0,r=0;
    m.insert(a[0]);
    while(l<n){      
        if(m.size()==3){
            r--;
            int sum=0;
            for(int i=l;i<=r;i++){
                if(a[i]==a[l])sum++;
                else sum--;
                v.push_back(sum);
            }m1[0]++;
            for(int i=0;i<v.size();i++){
                if(m1[v[i]]>0)ct+=m1[v[i]];
                m1[v[i]]++;
            }int ga=0;
            while(1){
                if(ga==0){
                    if(l!=r){l++;if(l==n)break;}
                    else ga=1;
                }else {
                    if(a[l]==a[l-1])l--;
                    else break;
                }
            }
        v.clear();
        m1.clear();
        m.erase(a[l-1]);
        }else{
            r++;
            m.insert(a[r]);
        }
    }
    cout<<ct-1<<endl;
}

总结:这一场我感觉主要是敢写暴力,敢打,更多是让算法偏向于数学或者是说规律,这一整场我认为是很好的,有思维有码力,考的也是比较平均,算是大部分针对蓝桥吧,后面三题考的就很xcpc分,知识点加上思维,这一整场我复盘下来收获了许多小知识点,可能我知道,也可能我会用,但是没这么直观的点评吧算是,写的还是比价基础的,基本上代码我也都直接再写了一遍感受了一下,怎么说呢,感觉我还是退步了,不能做到当时那样心无旁骛的all in,这场做的可能不是很专心,花了四五个小时(虽然看起来没多少对吧)我觉得我过段时间应该再复盘一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值