NOIP模拟1

赛时rank3,95,30,40,5,5

赛后hack,rank7,40,30,40,5,5

\(太CAI了\)

T1 分糖果

简要题意:
\(n\)个数分成最多组,使得每组有\(3\)个人,每组的数字和能被\(3\)整除,输出组数和方案

\(n≤10^5,1≤a_i≤10^5\)

\(solution:\)

将每个数\(\mod3\)存入,则有三类:余数为0,1,2;

可以的方案有三种\(0,0,0\)\(0,1,2\)\(1,1,1\)\(2,2,2\)

考虑到\(0,1,2\)不少于三个时无意义,所以枚举\(0,1,2\)的个数,时间复杂度\(O(n)\)

\(code:\)

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
const int N = 1e5 + 10;
int n,a[N];
queue<int> tot[3];
signed main(){
    #ifndef ONLINE_JUDGE
        infile("in.in");outfile("out.out");
    #else
    #endif
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0)->sync_with_stdio(false);
    cin>>n;
    for(int i = 1;i <= n; ++i) cin>>a[i];
    for(int i = 1;i <= n; ++i) tot[a[i]%3].push(i);
    int emm = min({tot[0].size(),tot[1].size(),tot[2].size()});
    ll ans = 0,res = 0;
    for(int i = 0;i <= min(emm,2); ++i){
        if(ans < (tot[0].size()-i)/3+(tot[1].size()-i)/3+(tot[2].size()-i)/3+i){
            ans = (tot[0].size()-i)/3+(tot[1].size()-i)/3+(tot[2].size()-i)/3+i;
            res = i;
        }
    }
    cout<<ans<<'\n';
    if(!ans) return 0;
    while(res--){
        cout<<tot[0].front()<<' '<<tot[1].front()<<' '<<tot[2].front()<<'\n';
        tot[0].pop();tot[1].pop();tot[2].pop();
    }
    while(tot[0].size()>=3){
        cout<<tot[0].front()<<' ';tot[0].pop();
        cout<<tot[0].front()<<' ';tot[0].pop();
        cout<<tot[0].front()<<'\n';tot[0].pop();
    }
    while(tot[1].size()>=3){
        cout<<tot[1].front()<<' ';tot[1].pop();
        cout<<tot[1].front()<<' ';tot[1].pop();
        cout<<tot[1].front()<<'\n';tot[1].pop();
    }
    while(tot[2].size()>=3){
        cout<<tot[2].front()<<' ';tot[2].pop();
        cout<<tot[2].front()<<' ';tot[2].pop();
        cout<<tot[2].front()<<'\n';tot[2].pop();
    }
}

T2 乒乓球

找循环节,不会打,待填

T3 与或

简要题意:

给定一个长度为\(n\)的数组\(a\,\)\((n\le 10^5)\),和\(k\)|运算符,\(n-k-1\)&运算符,用以上\(n-1\)个符号将数组连接起来,从左到右计算结果,使得结果最大并且符号序列的字典序最小,输出结果和符号序列。

样例输入1

4 2
1 3 5 7

样例输出1

7
||&

样例输入2

4 1
1 3 5 7

样例输出2

7
&&|

\(solution:\)

有一个结论:&放在|前一定不劣。

证明:假设有三个相邻的位置x,y,z,左边是,右边是&,那么结果最大是z,假设左边是&,右边是,那么结果最小是z

考虑按位贪心,对于当前位置,若放了|后后续最大答案仍是理论最大值,则放|,反之,则放&

\(code:\)

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
const int N = 2e5 + 10;
int n,k,sum[N][61];
ll a[N];
inline ll check(int pos,ll val,int k){
    if(pos <= n - k){
        vector<int> t(60);
        for(int i = 0;i < 60; ++i)
            t[i] = sum[n-k][i] - sum[pos-1][i];
        for(int i = 0;i < 60; ++i)
            if(t[i] != (n-k-pos+1) && ((val>>i)&1))
                val ^= (1ll<<i);
    }
    if(k >= 1){
        vector<int> t(60);
        for(int i = 0;i < 60; ++i) t[i] = sum[n][i] - sum[n - k][i];
        for(int i = 0;i < 60; ++i) if(t[i]) val |= (1ll<<i);
    }
    return val;
}
signed main(){
    #ifdef ONLINE_JUDGE
        infile("in.in");outfile("out.out");
    #else
    #endif
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0)->sync_with_stdio(false);
    cin>>n>>k;
    for(int i = 1;i <= n; ++i) cin >> a[i];
    for(int i = 1;i <= n; ++i){
        for(int j = 0;j < 60; ++j) sum[i][j] = sum[i-1][j];
        for(int j = 0;j < 60; ++j) sum[i][j] += ((a[i]>>j)&1);
    }
    ll mx = check(2,a[1],k);
    cout<<mx<<'\n';
    int nowk = k;ll ans = 1;
    for(int i = 2;i <= n; ++i){
        if(nowk >= 1 && check(i+1,ans|a[i],nowk-1) == mx){
            cout<<'|';
            ans |= a[i];
            nowk--;
        }
        else cout<<'&',ans &= a[i];
    }
}

T4 跳舞

\(solution:\)

\(ok_{i,j}\)表示\(i\sim j\)可以消去,利用区间dp

\[ok_{i,j} = \max ok_{i,k-1}\& ok_{k+1,j}\&[gcd(a[i-1],a[k])>1\,||\, gcd(a[k],a[j+1])>1] \]

再考虑设\(f_i\)表示钦定留下第\(i\)个,可以删掉几个人

则有

\[f_i = \max f_j + ((i-j-1)\&ok_{j+1,i-1}) \]

注意处理ok的边界,\(ok_{i,i-1}=1\)

\(code:\)

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
const int N = 510;
bitset<N> ok[N];
int n,a[N],f[N];
bitset<N> gcd[N];
signed main(){
    #ifndef ONLINE_JUDGE
        infile("in.in");outfile("out.out");
    #else
    #endif
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0)->sync_with_stdio(false);
    cin >> n;
    for(int i = 1;i <= n; ++i) cin >> a[i];
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= n; ++j)
            gcd[i][j] = (__gcd(a[i],a[j])>1);
    for(int i = 1;i <= n; ++i)
        if(gcd[i][i-1] || gcd[i][i+1]) ok[i][i] = true;
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j < i; ++j){
            ok[i][j] = true;
        }
    }
    for(int len = 2;len <= n; ++len){
        for(int i = 1;i + len - 1 <= n; ++i){
            int ed = i + len - 1;
            for(int j = i;j <= ed; ++j){
                if(ok[i][ed]) break;
                ok[i][ed] = (ok[i][j-1]&&ok[j+1][ed]&&(gcd[j][i-1]||gcd[j][ed+1]));
            }
        }
    }
    for(int i = 2;i <= n+1; ++i){
        for(int j = 0; j < i; ++j){
            f[i] = max(f[i],f[j]+(i-j-1)*ok[j+1][i-1]);
        }
    }
    cout<<f[n+1];
}

T5 音乐播放器

概率dp,不会……,待填

总结:
还是

\[CAI \]

posted @ 2024-07-09 10:18  CuFeO4  阅读(11)  评论(0编辑  收藏  举报