10月23日比赛

T1

看了一眼想到了暴力怎么写,但是看看数据范围,很明显就是给 \(O(n^2)\)\(O(n^2logn)\) 的,那么就好很自然的想到枚举两个确定另外一个。

性质

1.只要求下标递增,那么直接枚举即可无需排序。
2.可以开桶统计这个数出现在了那些位置,然后排序,排完序二分位置,这是在vector中size-pos就是答案。
3.开桶需要离散化。

复杂度

\(O(n^2logn)\),100pts

没开 \(long\ long\) 挂了30pts

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a[3077],ans;
vector<int>mp[3077];
map<int,int>xb;
int tot;
signed main() {
    cin>>n;
    for(int i = 1;i <= n;++ i) {
        scanf("%lld",&a[i]);
        if(!xb[a[i]]) xb[a[i]] = ++tot;
        mp[xb[a[i]]].push_back(i);
    }
    for(int i = 1;i <= tot;++ i) {
        sort(mp[i].begin(),mp[i].end());
    }
    for(int x = 1;x < n;++ x) {
        for(int y = x+1;y <= n;++ y) {
            int az = xb[__gcd(a[x],a[y])];
            if(!mp[az].size()) continue;
            else {
                int pos = upper_bound(mp[az].begin(),mp[az].end(),y)-mp[az].begin();
                ans += mp[az].size()-pos;
            }
        }
    }
    cout<<ans;
	return 0; 
}

T2

我自己清楚自己dp练的少,所以第一眼没看出来怎么写。

所以乱搞了一波。

\(f[i]\) 表示 \(1-i\) 的最长子序列的长度,同时用一个 \(set\) 去记录这个最长的长度的子序列的最后一个是多少。

我用这个 \(set\) 的目的是为了转移的时候可以将所有的合法状态都判断一次,这样就可以保证转移不漏。

然后这个dp,无论是状态设计还是转移都特别麻烦。

但是你不能说他不对,因为他拿到了80pts

正解

\(f[i]\) 表示以 \(i\) 为结尾的最长子序列的长度。

很显然这个状态设计就是限制了我最后一个必须选择 \(a_i\)
既这个子序列的最后一个必定是 \(a_i\),那么这个我们就可以方便的比较当前的和上一个,就不需要另开一个 \(set\) 去存上一个数是多少了,可以说是状态简单,转移简单。

100pts

状态设计确实很重要。

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
int n,m,a[1011];
struct op{
    int val;
    set<int>lt;
}f[1011];
int ans;
bool chck(int lim) {
    for(int i = 1;i <= n;++ i) f[i].val=1,f[i].lt.clear();
    f[1].val=1,f[1].lt.insert(a[1]);
    for(int i = 2;i <= n;++ i) {
        int res = 0;
        for(int j = 1;j < i;++ j) {
            if(abs(a[i]-a[j]) <= lim) 
                f[i].val = max(f[i].val,f[j].val+1);
        }
        if(f[i].val>=m) return 1;
    }
    return 0;
}
signed main() {
    cin>>n>>m;
    for(int i = 1;i <= n;++ i) {
        scanf("%lld",&a[i]);
    }
    if(chck(0)) {
        cout<<0;
        return 0;
    }
    ll l = 1,r = 2e9;
    while(l <= r) {
        ll mid = ((l+r)>>1);
        if(chck(mid)) r = mid-1,ans = mid;
        else l = mid+1;
    }
    cout<<ans;
    return 0;
}
posted @ 2022-10-23 22:47  双枫  阅读(27)  评论(0)    收藏  举报