2023牛客多校(9)

D

首先考虑枚举一个左端点

然后我们就会发现,对于一个位置来说,会影响它的只有前缀和后缀比它小的数

于是让每个数字不合法的都是一个区间

可以预处理$[L,i]$这个范围内有几个比它小的数,设为$x$

然后就能知道第一个让它不合法的位置($i - L - x$)个比它小的数的位置

而让它重新合法只需要再有一个比它小的数即可

于是我们就定义$f[i][j]$表示$i$后第$j$个比$a_i$小的数的位置,预处理这个即可。

最后就是一个线段覆盖问题,扫描线瞎跑一下

$O(n^2)$

#include <bits/stdc++.h>
using namespace std;
int a[5005][5005];
int T;
const int mx = 5000;
int pp[5005];
int Tree[5005];
int aa[5005];
int Sum[5005][5005];
int main(){
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while (T--){
        int N;
        cin >> N;
        for (int i = 1 ; i <= N ; i ++)
            cin >> aa[i];
        for (int i = 1 ; i <= N ; i ++)
            for (int j = 1 ; j <= N ; j ++)
                a[i][j] = 0;
        for (int i = 1 ; i <= N ; i ++){
            int cnt = 0;
            a[i][0] = i;
            for (int j = i + 1 ; j <= N ; j ++){
                if (aa[j] < aa[i]){
                    cnt ++;
                    a[i][cnt] = j;
                }
            }
        }
        for (int i = 1 ; i <= N ; i ++){
            for (int j = 1 ; j <= i ; j ++){
                if (aa[j] < aa[i]) Sum[i][j] = Sum[i][j-1] + 1;
                else Sum[i][j] = Sum[i][j-1];
            }
        }
        int ans = 0;
        for (int i = 1 ; i <= N ; i ++){
            for (int j = i ; j <= N ; j ++)
                pp[j] = 0;
            for (int j = i ; j <= N ; j ++){
                int x = Sum[j][j] - Sum[j][i-1];
                int Len = j-i+1;
                int y = Len - x-1;
                int y1 = Len - x;
                int x1 = a[j][y];
                int x2 = a[j][y1];
                if (x1 == 0) x1 = N+1;
                if (x2 == 0) x2 = N+1;
                //cout << i << " " << j << " " << x1 << " " << x2 << endl;
                pp[x1] ++;
                pp[x2]--;
            }
            int cnt = 0;
            for (int j = i ; j <= N ; j ++){
                cnt = cnt + pp[j];
                if (cnt == 0) ans ++;
            }
        }
        cout << ans << endl;
    }
    return 0;
} 

E

辗转相除一定能构造一个合法方案。

#include <bits/stdc++.h>

#define LL long long
#define ULL unsigned long long
#define x first
#define y second
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int INF = 0x3f3f3f3f;

vector<array<int, 3>> ans;

void dfs(int x, int y, int n, int m)
{
    if (n == 0 || m == 0)
        return;
    if (n > m)
    {
        int t = n / m;
        int d = n % m;
        for (int i = 0; i < t; i++)
        {
            ans.push_back({x + m * i, y, m});
        }
        dfs(x + m * t, y, d, m);
    }
    else
    {
        int t = m / n;
        int d = m % n;
        for (int i = 0; i < t; i++)
        {
            ans.push_back({x, y + n * i, n});
        }
        dfs(x, y + n * t, n, d);
    }
}

void solve()
{
    int n, m;
    cin >> n >> m;

    ans.clear();

    cout << "YES" << endl;
    dfs(0, 0, n, m);

    cout << ans.size() << endl;
    for (int i = 0; i < ans.size(); i++)
    {
        cout << ans[i][0] << ' ' << ans[i][1] << ' ' << ans[i][2] << endl;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;

    while (t--)
    {
        solve();
    }

    return 0;
}

I

有点不好解释的一个题

首先考虑把所有线段端点都压到数轴上

然后每条线段的覆盖情况无非三个情况:

1、无线段覆盖

2、有一条

3、有两条

分开统计一下这三种情况,并且记录当前枚举的点被几个线段覆盖

然后我们就只需要在每个线段离开的时候去统计它的贡献。如果当前离开的时候第$i$组是两条,并且有$n$覆盖的话,那么答案就加上$2^{cnt_2 -1}$

如果是$1$条,就加上$2^{cnt_2}$

因为我们贪心的想,每个线段一定是尽可能覆盖,直到不能再覆盖了才去统计答案。这样能保证不重不漏

然后扫描线扫一下就好啦(

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 1e9+7;
struct Node{
    int x,y,Type;
};
int temp(Node a,Node b){
    if (a.x == b.x && a.Type == b.Type){
        return a.y < b.y;
    }
    if (a.x == b.x){
        return a.Type < b.Type;
    }
    return a.x<b.x;
}
int Pow(int x,int y){
    int ans = 1;
    for (;y;y>>=1){
        if (y & 1) ans = 1ll * ans * x%MOD;
        x = 1ll * x * x%MOD;
    }
    return ans;
}
int cnt[500005];
vector<Node> nw;
signed main(){
    int N;
    cin >> N;
    for (int i = 1 ; i <= N ; i ++){
        int l,r;
        cin >> l >> r;
        nw.push_back({l,i,1});
        nw.push_back({r+1,i,-1});
        cin >> l >> r;
        nw.push_back({l,i,1});
        nw.push_back({r+1,i,-1});
    }
    sort(nw.begin(),nw.end() ,temp);
    int cnt1 = 0,ans = 0 ,ans1 = 0,cnt2 = 0;
    for (auto v : nw){
        int pos = v.x,bh = v.y,tp = v.Type;
        if (tp == 1){
            if (cnt[bh] == 0) {
                cnt1 ++;
                cnt[bh] ++;
            }else
            if (cnt[bh] == 1){
                cnt[bh] ++;
                cnt2 ++;
            }
        }else{
            if (cnt[bh] == 1){
                if (cnt1 == N) ans = (ans + Pow(2,cnt2))%MOD;
                cnt[bh]--;
                cnt1--;
            } else {
                cnt[bh]--;
                if (cnt1 == N) (ans += Pow(2,cnt2-1))%=MOD;
                cnt2--;
            }
        }
    }
    cout << ans%MOD << endl;
    return 0;
}

G线代水平不行,没想出来

B数论水平不行,推慢了。

令人感慨(

posted @ 2023-08-14 20:41  si_nian  阅读(39)  评论(0编辑  收藏  举报