C. Min Max Sort(edu142)

题意:给定一个序列,有操作OP,是选择两个数字,其中大的放到末尾,小的放到起点,问最少多少次操作使序列是递增的

思路:我们可以发现,当我们进行其他操作时,最后一次操作必然是对1和n进行操作,那么倒数第二次操作就是将2和n-1进行操作,依次类推,我们假设操作k次,那么1~k和k+1~n-k都会被排序,假设n为6,我们操作的顺序是 3 4,2 5,1 6,我们初始k为n>>1=3,我们对3 4检查时,如果发现3 4符合条件,则k--,代表3 4 这一步不需要操作,然后检查2 5,依次进行,如果有一步x不符合条件,那么直接break,我们可以发现,<=x的数字都得进行操作,那么操作次数k就等于x

diamond:

#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve() {
    int n;
    cin >> n;
    vector<int> vis(n + 1);
    for (int i = 1; i <= n; ++i) {
        int x;
        cin >> x;
        vis[x] = i;
    }
    int ans = n >> 1;
    while (ans && vis[ans] < vis[ans + 1] && vis[n - ans + 1] > vis[n - ans])ans--;
    cout << ans << '\n';
}
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
}

C. Tea Tasting(edu143)

题意:给了n杯茶和n个人,每杯茶有ai的茶水,每个人每一轮最多喝bi的茶水,第一轮的喝茶顺序是i喝第i杯,第二轮的顺序是第i个人喝第i-1杯,第三轮第i个人喝第i-2杯茶,这样进行n轮,问n轮后每个人最多喝了多少茶。

思路:我们可以发现第i杯茶,只会被第>=i的人依次喝下去,那么我们可以算出每一杯茶会被哪些人喝,比如第2杯茶只能被2 3 4人喝,那么2 3 4 就记录了一次,他们都喝了一次bi的茶水,那么就记录喝了几次最大值,第五个人无法喝b5,那么就把剩下的这个加到ans5之中,不记录次数,最后的答案$ans[i]=ans[i]+cnt[i]* b[i]$,记录cnt,我们可以发现区间的增加,我们可以用差分,树状数组等结构来解决,这边我用的是差分来解决

diamond:

#include<bits/stdc++.h>
using namespace std;
#define int long long///
void solve() {
    int n;
    cin>>n;
    vector<int>a(n+6),b(n+6);
    for (int i = 1; i <=n ; ++i) {
        cin>>a[i];
    }
    for (int i = 1; i <=n ; ++i) {
        cin>>b[i];
    }
    vector<int>sum(n+1,0);
    for (int i = 1; i <=n ; ++i) {
        sum[i]=sum[i-1]+b[i];
    }
    vector<int>d(n+2);
    vector<int>ans(n+1);
    for (int i = 1; i <=n ; ++i) {
        int r= lower_bound(sum.begin()+i,sum.end(),a[i]+sum[i-1])-sum.begin();
//        cout<<r<<endl;
        if(r>i){
            d[i]++;
            if(r<=n){
                d[r]--;
            }
        }
        if(r<=n){
            ans[r]+=a[i]-(sum[r-1]-sum[i-1]);
        }
    }
    for (int i = 2; i <=n ; ++i) {
        d[i]+=d[i-1];
    }
    for (int i = 1; i <=n ; ++i) {
        ans[i]+=d[i]*b[i];
        cout<<ans[i]<<" \n"[i==n];
    }

}
int32_t main() {
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t=1;
    cin>>t;
    while (t--){
        solve();
    }
}

C. Maximum Set(edu144)

题意:从l到r中选择某些数组成数组,数组之间任意两个数x,y满足$x%y==0||y%x==0$,数组个数最大是多少,可以有多少个方案形成这个大小的数组

思路:任意两个数都可以互相除,那么我们可以这样考虑,x,2x,4x,8x都乘2,那么其实也可以乘3,只要个数等于K就可以,乘>=4就不可以了,因为4=2 2,乘的越大,个数越小,那么题意就是可以变成,可以×几个3,假如个数为6,可以×2个3,那么ans+=1+5 1 + 4 * 2。依次进行累加

diamond:

#include<bits/stdc++.h>
using namespace std;
#define int long long///
const int mod =998244353;
typedef pair<int,int>PII;
const int N=1e5+5,M=1e5+5,INF=0x3f3f3f3f,Mod=1e9+7;
const double eps=1e-8;
typedef long long ll;
ll a,b;
int n,p;
int ksm(int x,int y,int p){
    int res=1;
    while(y){
        if(y&1)res=(ll)res*x%p;
        x=(ll)x*x%p;
        y>>=1;
    }
    return res;
}
int c(int a,int b,int p){
    if(b>a)return 0;
    int res=1;
    for(int i=1,j=a;i<=b;++i,--j){
        res=(ll)res*j%p;
        res=(ll)res*ksm(i,p-2,p)%p;
    }
    return res;
}
int Lucas(ll a,ll b,int p){
    if(a<p&&b<p)return c(a,b,p);
    return (ll)c(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
}
void solve() {
    int l, r;
    cin >> l >> r;
    int cnt = 0;
    int x = l;
    while (x <= r) {
        cnt++;
        x *= 2;
    }
    int ans = 0, pp = 1;
    int lst = l;
    for (int i = cnt - 1; i >= 0; --i) {
        int g = ksm(3, i, mod) * ksm(2, cnt - 1 - i, mod);
        if (r / g < l) {
            continue;
        }
        int cg = c(cnt, i, mod);
        ans = (ans + cg * (r / g - lst + 1)) % mod;
        lst = r / g + 1;
    }
    cout << cnt << ' ' << ans << '\n';
}
int32_t main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
}

D. Triangle Coloring(edu143)

题意:给n/3个三角形,n为6的倍数,将三角形的三个节点涂色,给出每条边的权重,涂色保证黑为n/2个,白为n/2个,假设相邻两个点为不同颜色,这这条边的权值被加到ans之中,求最大的贡献值ans

思路:假设权值为 2 3 4 ,我们把3,4这两个相邻的点涂成同一颜色,另一个点涂成另外一个颜色,每一个三角形都类似,最后我们可以发现,第一个三角形是112,那么第二个三角形可以是221,符合五五开的原则,我们可以发现如果是k个三角形,那么一半是112,另一半是221,所以刚开始ans=C(k,k/2);三角形边有四种大小情况

  • 1 1 1,ans=ans * 3

  • 1 1 2,ans=ans * 2

  • 1 2 3 ,ans=ans

  • 2 2 1,ans=ans

    diamond:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long///
    const int mod =998244353;
    typedef pair<int,int>PII;
    const int N=1e5+5,M=1e5+5,INF=0x3f3f3f3f,Mod=1e9+7;
    const double eps=1e-8;
    typedef long long ll;
    ll a,b;
    int n,p;
    int ksm(int x,int y,int p){
    int res=1;
    while(y){
        if(y&1)res=(ll)res*x%p;
        x=(ll)x*x%p;
        y>>=1;
    }
    return res;
    }
    int c(int a,int b,int p){
    if(b>a)return 0;
    int res=1;
    for(int i=1,j=a;i<=b;++i,--j){
        res=(ll)res*j%p;
        res=(ll)res*ksm(i,p-2,p)%p;
    }
    return res;
    }
    int Lucas(ll a,ll b,int p){
    if(a<p&&b<p)return c(a,b,p);
    return (ll)c(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
    }
    void solve() {
    cin>>n;
    int k=n/3;
    int ans= c(k,k/2,mod);
    for (int i = 0; i < n/3; ++i) {
        int x,y,z;
        cin>>x>>y>>z;
        map<int,int>mp;
        mp[x]++;
        mp[y]++;
        mp[z]++;
        if(mp.size()==1){
            ans=(ans*3%mod)%mod;
        }
        if(mp.size()==2){
            if(mp.begin()->second==2)
            ans=ans*2%mod;
        }
    }
    cout<<ans<<'\n';
    }
    int32_t main() {
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t=1;
    //cin>>t;
    while (t--){
        solve();
    }
    }

    C. Set or Decrease(edu120)

    题意:给定一个数组,有操作OP,你有两个选择,OP1为把一个数字减少1,OP2 将ai变成aj,问最少进行多少次操作数组的和才能<=k

    思路:我们可以发现,我们若进行OP2,必然是将最大的变为最小的,所以我们就可以枚举OP2的次数y,然后O1求出满足k时的OP1的次数x,x+y取min即可

    diamond:

    #include<bits/stdc++.h>
    using namespace  std;
    #define int long long
    #define lowbit(x) ( x & -x )
    #define endl '\n'
    int t;
    int cnt=1;
    void solve(){
    int n,k;
    cin>>n>>k;
    vector<int>a(n);
    int sum=0;
    for (int i = 0; i <n ; ++i) {
        cin>>a[i];
        sum+=a[i];
    }
    if(sum<=k){
        cout<<"0\n";
        return;
    }
    if(n==1){
        cout<<max(a.back()-k,0ll)<<'\n';
        return;
    
    }
    //    cout<<sum<<endl;
    int ans=1e15;
    sort(a.begin(),a.end(),greater<int>());
    int sum1=0;
    if(sum-1<=k){
        cout<<"1\n";
        return;
    }
    if(sum-*a.begin()+a.back()<=k){
        cout<<"1\n";
        return;
    }
    for (int i = 0; i <n-1 ; ++i) {
        sum1+=a[i];
    //        cout<<sum1<<"gg";
        int y=i+1;
        int x=(sum-sum1-k+y*a.back()+y)/(y+1);
    //        cout<<y<<' '<<x<<endl;
        if(x<0)x=0;
    //        cout<<y<<' '<<x<<endl;
    //        x--;
    //        if(sum-sum1+y*a.back()-y*x<=k)
    
        ans=min(y+x,ans);
    }
    cout<<ans<<'\n';
    }
    signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    for (int i = 1; i <=t ; ++i) {
        cnt++;
        solve();
    }
    }

    C. Sum on Subarrays(edu145)

    题意:给整数n和k,构造一个n大小的数组,使得k个连续子数组的和为整数,其他都为负数

    思路:我们可以这样构造,前面都是2,截至点往后都构造为最小-1000,这样可以保证如果前x个是2,那么可以形成$x * (x+1)/2$个区间,所以我们可以枚举找到第一个ans>=k的x,然后将1到x-1变为2,然后再计算k-x缺几个和为整的,我们可以这样想,2 2 2 m,m为1,则可再形成4个,如果m等于-1,那么可再形成3个,如果m=-3,则可以再形成两个,如果m=-5,则可以再形成1个,我们可以O(1)求出m的值,然后后面的数字全部填成 -1000即可

    diamond:

    #include<bits/stdc++.h>
    using namespace std;
    const double g=9.8;
    struct complex1{
    int a,b;
    };
    void solve(){
    int n,k;
    cin>>n>>k;
    if(!k){
        for (int i = 0; i < n; ++i) {
            cout<<"-1 ";
        }
        cout<<"\n";
        return;
    }
    int cnt=0;
    for (int i = 1; i <=n ; ++i) {
        int d=(i*(i+1)/2);
        if(d>=k){
            cnt=i;
            break;
        }
    }
    vector<int>a(n+1);
    for (int i = 1; i <cnt ; ++i) {
        a[i]=2;
    }
    if(cnt*(cnt+1)/2==k)a[cnt]=2;
    else{
        int cs=cnt*(cnt+1)/2-k-1;
        a[cnt]=-1-2*cs;
    }
    for (int i = cnt+1; i <=n ; ++i) {
        a[i]=-1000;
    }
    for (int i = 1; i <=n ; ++i) {
        cout<<a[i]<<' ';
    }
    cout<<'\n';
    }
    int main(){
    int t=1;
    cin>>t;
    while(t--){
        solve();
    }
    }

    D. Binary String Sorting(edu145)

    题意:给定一个01串,有两种操作,OP1是将相邻两个数交换,代价是1e12,OP2是将这个数字删除,代价是1e12+1,问将串变成非递减的串,代价最少是多少

    思路:观察串000101001,我们对第一个10交换之后,由于后面还是有0,要么我们就把这些0换到1之前,要么就删除,由于后面的0的位置与1的位置差是大于2的,那么次数就大于2,那么我们就直接删除更优,前缀的1同理,再与不移动的都删除相比,取更小的。那么其实交换最多就只能交换一个位置,其他位置不符合就删除,枚举一下交换的位置即可

    diamond:

    #include<bits/stdc++.h>
    using namespace std;
    const double g=9.8;
    #define int long long
    struct complex1 {
    int a, b;
    };
    const int a=1e12,b=1e12+1;
    void solve() {
    string s;
    cin>>s;
    s=" "+s;
    int n=s.size()-1;
    s=s+" ";
    vector<int>pre1(n+1),suf0(n+2);
    for (int i = 1; i <=n ; ++i) {
        pre1[i]=pre1[i-1]+(s[i]=='1');
    }
    for (int i = n; i >=0 ; --i) {
        suf0[i]=suf0[i+1]+(s[i]=='0');
    }
    int ans=1e18;
    
    for (int i = 1; i <=n ; ++i) {
        ans=min(ans,(pre1[i-1]+suf0[i+1])*b);
    }
    int shan1=0,shan0=0;
    int x=0,y=0;
    for (int i = 1; i <=n ; ++i) {
        if(s[i]=='1'&&s[i+1]=='0'){
            ans=min(ans,a+(pre1[i-1]+suf0[i+2])*b);
        }
    }
    cout<<ans<<'\n';
    }
    signed main() {
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    }

    C. Yet Another Tournament(edu141)

    题意:你去参加锦标赛,有n个对手编号1~n,每个人可以战胜编号在他前面的所有选手,如果你要战胜他们,那么需要准备时间,战胜每个人的你需要的准备时间是ai,你一共可以准备m时间,按照胜场排名,问你最低可以排到第几名呢?

    思路:按照ai排序,然后你拿下前j个,如果前j个之中有第j号选手,那么你就可以战胜j个选手,那么ans=n-j+1,我们是按0~n-1,同理注意一下就好,如果没有第j号选手,那么我们不选择拿下排序后的j-1选手,然后恢复体力判断是否可以拿下第j位选手,如果拿下,胜场相同,ans还是n-j+1,如果拿不下,排名ans则位n-j+1+1,前缀和二分或者On求出j即可

    diamond:

    
    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    struct complex1 {
    int a, b;
    };
    int k=0;
    const int a=1e12,b=1e12+1;
    void solve() {
    int n,m;
    cin>>n>>m;
    vector< int >g(n);
    vector<pair<int,int>>gg(n);
    for (int i = 0; i <n ; ++i) {
        cin>>g[i];
        gg[i]={g[i],i};
    }
    sort(gg.begin(),gg.end());
    int sum=m,x=0;
    for (int i = 0; i <n ; ++i) {
        if(sum>=gg[i].first){
            sum-=gg[i].first; x++;
        }
        else break;
    }
    if(x==0) {
        cout << n + 1 << endl;
        return;
    }
    int flag=0;
    for (int i = 0; i <x ; ++i) {
        if(gg[i].second==x){
            flag=1;
            break;
        }
    }
    if(flag){
        cout<<max(1ll,n-x)<<endl;
        return;
    }
    //    cout<<sum<<endl;
    if(sum+gg[x-1].first>=g[x]){
        cout<<max(1ll,n-x)<<endl;
        return;
    }
    cout<<n-x+1<<endl;
    }
    signed main() {
    int t = 1;
    cin >> t;
    for (int i = 1; i <=t ; ++i) {
        k++;
        solve();
    }

}

posted on 2023-09-09 12:57  IR101  阅读(9)  评论(0编辑  收藏  举报  来源