10月训练(部分

复制代码
//http://codeforces.com/problemset/problem/1690/E
//利用set数组 里面储存的是 每一个数字%k之后的数字,由于特殊性,一个数大于k,那么就算这个数+0他对答案的贡献最少也是 m/k的价值
//所以可以直接计算价值,然后第二位储存编号,便于分辨
//遍历set数组,取第一s,由于set数组内自动排序,所以利用内置二分找出第一个大于等于 k-i的数,由于两者加起来是最接近k的,所以损失一定最小,答案一定最大

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
int n,t,a[N],f[N],res,num,ans,m,k;
bool vis[N];
void solve()
{
    cin>>n>>k; res=0;
    set<pair<int,int>>s;
    for(int i=1;i<=n;i++){
        cin>>m;
        res+=m/k,s.insert({m%k,i});
    }
    while(!s.empty()){
        auto it=s.begin();
        auto x=*it;
        s.erase(it);
        auto it2=s.lower_bound({k-x.first,-1});
        if(it2==s.end()) it2=s.begin();
        auto y=*it2;
        s.erase(it2);
        if(x.first+y.first>=k) res++;
    }
    cout<<res<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}
复制代码
复制代码
//http://codeforces.com/problemset/problem/1492/C

//正向扫一遍,求出这个字符串中第一个出现模板串中字符的位置,并记录在h数组
//反向扫一遍,记录在e数组
//对于模板里面的每一个字符,由于我们需要让代价最大,即两相邻字符的间距最大
//那么对于每一个字符,他与上一次字符的最大距离一定是,上一个字符第一次出现的位置和这个字符最后出现位置的差值,取最大值即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s,ss;
int n,t,h[N],e[N],res,num,ans,m,k;
bool vis[N];
void solve()
{
    cin>>n>>m>>s>>ss;
    for(int i=0,j=0;i<n;i++)
        if(s[i]==ss[j]) h[j++]=i;
    for(int i=n-1,j=m-1;i>=0;i--)
        if(s[i]==ss[j]) e[j--]=i;
    for(int i=1;i<m;i++) res=max(res,e[i]-h[i-1]);
    cout<<res;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}
复制代码
复制代码
//https://codeforces.com/problemset/problem/1680/C

//由于我们要去前去后,所以我们最后得到的一个区间为[l,r],贪心的去想
//对于一个固定的l,我们的r越大,则1就越少,0就越多,即区间内代价越大,区间外代价越小
//那么令r为n,则有,r越小,0越少,1越多,即代价递增,所以代价满足单调性,对代价二分即可

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s;
int n,t,g[N],f[N],res,num,ans,m,k;
bool vis[N];
bool check(int u)
{
    for(int l=1,r=0;l<=n;l++){
        while(r<n&&f[r]-f[l-1]<=u) r++;
        if(g[n]-g[r]+g[l-1]<=u) return true; 
    }
    return false;
}
void solve()
{
    cin>>s;
    int l=0,r=s.size();
    n=s.size();
    for(int i=1;i<=s.size();i++) f[i]=f[i-1]+(s[i-1]=='0'),g[i]=g[i-1]+(s[i-1]=='1');
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<r<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

//基于上份代码的思路,由于我们固定l之后,r从小到大增加,会得到区间内代价变大,区间外代价变小
//所以当我们的区间内代价最接近区间外代价的时候,此时一定是答案,所以就有如下结论:
//1_delete=0_live,1_live+0_live=1_live+1_delete=1_size;
//所以我们只需要维护一个长度为 字符串中1的长度的窗口,然后等价滑窗即可

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s;
int n,t,a[N],f[N],res,num,ans,m,k;
bool vis[N];
void solve()
{
    cin>>s; res=0,num=0;
    for(int i=0;i<s.length();i++) if(s[i]=='1') num++;
    int num0=0,num1=num;
    for(int i=0;i<num;i++){
        if(s[i]=='0') num0++;
        else num1--;
    }
    res=max(num1,num0);
    for(int i=num;i<s.length();i++){
        if(s[i]=='0') num0++;
        else num1--;
        if(s[i-num]=='0') num0--;
        else num1++;
        res=min(res,max(num1,num0));
    }
    cout<<res<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}
复制代码
复制代码
//https://www.luogu.com.cn/problem/P8700
// 观察规律,只需要看最内层的,从里到外的周期分别为 4 8 12,所以内层为最小周期
//所以对于内层每一个块,它的对应块是确定的,枚举并分析即可
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
string st,ed,mid;
void solve()
{
    map<char,int>mp;
    cin>>st>>mid>>ed;
    for(int i=0;i<4;i++){
        mp[st[i]]++,mp[st[4+i]]++,mp[st[8+i]]++,mp[mid[i]]++,mp[mid[4+i]]++,mp[ed[i]]++;
        if(mp['Y']!=1||mp['R']!=2||mp['G']!=3) return cout<<"NO"<<endl,void();
        mp.clear();
    }
    cout<<"YES"<<endl;
}
int main()
{
    int t; cin>>t;
    while(t--) solve();
}
复制代码

 

复制代码
//https://codeforces.com/problemset/problem/1678/B2

//首先考虑如何求出最小次数,可以把连串分成两个一份的串,可以发现,如果出现'01'或者'10',我们就需要变,求出次数即可
//还可以这样考虑,例如: 001010110001,可以变成 2 1 1 1 1 2 3 1,然后从第一位开始,如果超过2,就向后+1
// 2 1 1 1 1 2 3 1
// 2 0 2 1 1 2 3 1
// 2 0 2 0 2 2 3 1
// 2 0 2 0 2 2 2 2,记录变换次数即可

//接下来考虑最小段数,贪心的去想,对于任何一个要改变的串,我们可以把它变成'00'或者'11',那么我们肯定是要把他们改成对结果没有影响的串
// 例如: 101000 //10 10 00,
//则10在修改的时候,我们都尽量的变成一开始就连续的串,即'00'
//所以我们先找到第一个连续的串,然后记录,然后遍历串,如果当前需要修改,就修改成i-1的串,因为前i个都是连续的,这样做一定不会增加次数

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s,ss;
int n,t,a[N],f[N],res,num,ans,m,k,cnt;
bool vis[N];
void solve()
{
    cin>>n>>s; res=0,ans=1,ss=s,num=1;
    char x;
    for(int i=0;i<n;i+=2)
        if(s[i]==s[i+1]) {x=s[i];break;} 
    if(s[0]!=s[1]) s[0]=s[1]=x,res++;
    for(int i=2;i<n;i+=2)
        if(s[i]!=s[i+1]) s[i]=s[i+1]=s[i-1],res++; 
    x=s[0];
    for(int i=1;i<n;i++)
        if(s[i]!=x) ans++,x=s[i];
    cout<<res<<' '<<ans<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}
//另一个版本:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    for (cin >> t; t; t -= 1) {
        int n, L = -1, x = 0, y = 0;
        string s;
        cin >> n >> s;
        for (int i = 0; i < n; i += 2) {
            if (s[i] != s[i + 1]) x += 1;
            else {
                if (L != s[i]) y += 1;
                    L = s[i];
                }
            }
        cout << x << " " << max(y, 1) << "\n";
    }
}

//Dp思路:
    #include<bits/stdc++.h>
    #define fre(z) freopen(z".in","r",stdin),freopen(z".out","w",stdout)
    #define lowit(x) (x&-x)
    
    #define range(x) begin(x), end(x)
    #define sz(x) (int)(x).size()
    #define pb push_back
    
    #define sto                             \
        std::ios::sync_with_stdio(false);   \
        std::cin.tie(nullptr);              \
        std::cout.tie(nullptr);
        
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll,ll> PII;
    
    inline ll read(){
        ll x=0;char ch;bool f=true;
        for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')f^=f;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
        return f?x:-x;
    }
    //#define LOCAL_DEFINE
    #define DEBUG(x) cerr<<(#x)<<'='<<(x)<<endl
    const int N=2e5+7;
    const int INF=1e9;
    char s[N];
    int c[N];
    PII dp[N][2];
    void solve(){
        int n=read();
        scanf("%s",s+1);
        for(int i=1;i<=n;i++)s[i]-='0';
        for(int i=1;i<=n/2;i++)c[i]=s[i*2]+s[i*2-1]*2;
        for(int i=1;i<=n/2;i++)for(int j=0;j<2;j++)dp[i][j]={INF,INF};
        for(int i=1;i<=n/2;i++)for(int j=0;j<2;j++){
            int cc=c[i],dd=j?3:0,cnt=((dd%2)^(cc%2))+((dd/2)^(cc/2));
            for(int k=0;k<2;k++)
                dp[i][j]=min(dp[i][j],{dp[i-1][k].first+cnt,dp[i-1][k].second+(j!=k)});
        }
        PII ans=min(dp[n/2][0],dp[n/2][1]);
        cout<<ans.first<<" "<<ans.second+1<<"\n";
    }
    
    int main(){
        #ifdef ONLINE_JUDGE
        #else    
            //fre("1");
        #endif
        ll T=1;
        T=read();
        for(int i=1;i<=T;i++)solve();
        #ifdef LOCAL_DEFINE
            cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
        #endif
        return 0;
    }
/*
6
101000
6
011000
6
111000
6
000100
6
100100
*/
复制代码

 

复制代码
// https://codeforces.com/problemset/problem/1753/B

// 考虑一个性质: x!*(x+1)=(x+1)!
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,k,a[N],m;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>m,a[m]++;
    for(int i=1;i<k;i++){
        a[i+1]+=a[i]/(i+1),a[i]%=(i+1);
        if(a[i]!=0) return cout<<"NO"<<endl,0;
    }
    if(a[k]) cout<<"Yes"<<endl;
    else cout<<"NO"<<endl;
    return 0;
}
复制代码
复制代码
//https://codeforces.com/problemset/problem/1878/E

//思路很简单,静态区间,区间值不变,区间和固定,由于复杂度可想到快速求区间固定值,如st表树状数组等等
//由于已给出固定L,R的范围满足(L+1,n)单调性,对R进行二分即可,复杂度(N*logN)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s;
int n,t,a[N],f[N][25],res,lg[N],m,k;
bool vis[N];
bool check(int l,int r,int num)
{
    int pos=lg[r-l+1];
    if((f[l][pos]&f[r-(1<<pos)+1][pos])>=num) return true;
    else return 0;
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>f[i][0];
    for(int j=1;j<=lg[n];j++)
        for(int i=1;i<=n-(1<<j)+1;i++)
            f[i][j]=f[i][j-1]&f[i+(1<<(j-1))][j-1];
    cin>>m;
    while(m--){
        int x,k; cin>>x>>k;
        int l=x,r=n;
        if(f[x][0]<k){
            cout<<-1<<' ';
            continue;
        }
        while(l<r){
            int mid=(l+r+1)>>1;
            if(check(x,mid,k)) l=mid;
            else r=mid-1;
        }
        cout<<l<<' ';
    }
    cout<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    for(int i=2;i<=N;i++) lg[i]=lg[i>>1]+1;
    while(t--){
        solve();
    }
    return 0;
}
复制代码
复制代码
//https://www.luogu.com.cn/problem/P1280

//设 f[i]为时间为i时能获得的最大摸鱼时间
//正难则反,考虑从最后向前推,对于每一个时间ti,我们都有 f[ti]=max(f[ti],f[v[t[i]]]),v[t[i]]是在ti时接取任务之后需要多久的时间
//建立时间复杂度O(NM)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
int n,t,a[N],f[N],res,num,ans,m,k;
vector<int>pos[N];
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
     cin>>n>>k;
     for(int i=1;i<=k;i++){
        int x,y;cin>>x>>y;
        pos[x].push_back(y);
     }
     for(int i=n;i>=1;i--){
        if(pos[i].size()>0){
            for(int j=0;j<pos[i].size();j++)
                f[i]=max(f[i],f[i+pos[i][j]]);
        }
        else f[i]=f[i+1]+1;
     }
    cout<<f[1]<<endl;
    return 0;
}
复制代码
复制代码
//https://codeforces.com/problemset/problem/1792/C

//考虑到一个一定的顺序: 最大的数一定是最后一次被交换,倒数第二大的数倒数第二次被交换
//所以有 : 1,n一定是最后一次,那么我们去掉[1,n],此时有[2,n-1],发现是一个递归的规律
//利用f数组,来记录f[i]所包含的最长的连续排列长度,如果不符合就需要交换

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s;
int n,t,a[N],f[N],res,num,ans,m,k;
bool vis[N];
void solve()
{
    cin>>n; res=0;
    for(int i=1;i<=n;i++) cin>>a[i],f[i]=0;
    for(int i=1;i<=n;i++) f[a[i]]=f[a[i]-1]+1;
    for(int i=n/2;i>=1;i--) if(f[n-i+1]<n-i+1-i+1) res++;
    cout<<res<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}
复制代码
复制代码
//https://www.luogu.com.cn/problem/P1326
//非常考察细节,建议多看
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
int n,t,f[N],res,num,ans,m,k,s;
bool vis[N];
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    while(cin>>s>>t>>n){
        res=0,ans=0;
    if(n==1){
        if(s==t) cout<<1<<' '<<1<<endl;
        else if(s>t) cout<<3<<' '<<3<<endl;
        else cout<<0<<' '<<0<<endl;
    }
    else{
        // 最大得分:
        if(s>=n){
            int x=s-n+1;
            if(x>t) res+=3;
            else if(x==t) res+=1;
            res+=(n-1)*3;
        }
        else{
            res+=s*3,res+=n-s-1;
            if(!t) res++; //如果t没有的话
        }
        // 计算最小得分 :
        if(s<=t){
            int a=3,b=0;
            if(t<n-1) a+=n-1-t; //第一局输之后一直都是b赢
            if(n>t-s) b=n-(t-s); //前面一直平局,然后最后的得分
            ans=min(a,b);
        }
        else{
            ans+=3;
            if(t<n-1) ans+=n-t-1;
        }
        cout<<res<<' '<<ans<<endl;
    }
    }
    return 0;
}
复制代码
复制代码
//https://codeforces.com/contest/1539/problem/D

//考虑贪心,按照购买到一定的优惠次数进行排序,可以去贪心的想,我们要尽可能的吃到优惠
//在要求购买次数越小的情况下,我们就越有可能吃到优惠,所以当次数不够时,应优先买要求购买次数的大的物品
//因为对比要求购买次数小和购买次数大,肯定是要求购买次数小的物品更容易吃到优惠,而且由于我们的购买次数是一定的
//所以要求购买次数大的吃到的优惠次数是一定的,双指针即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
string s;
int n,t,a[N],f[N],res,ans,m,k,cur;
bool vis[N];
struct node
{
    int num,cnt;
    bool operator<(const node&w)const
    {
        if(cnt==w.cnt) return num<w.num;
        else return cnt<w.cnt;
    }
}item[N];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>item[i].num>>item[i].cnt;
    sort(item+1,item+1+n);
    for(int i=1,j=n;i<=j;i++){
        while(i<=j&&cur<item[i].cnt){
            int x=min(item[j].num,item[i].cnt-cur);
            res+=x*2,cur+=x,item[j].num-=x;
            if(item[j].num==0) j--;
        }
        res+=item[i].num,cur+=item[i].num;
    }
    cout<<res<<endl;
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}
复制代码
复制代码
//https://www.luogu.com.cn/problem/P8773

//异或具有的性质: a⊕b=x⇔b=x⊕a
//得知这个性质之后我们可以预处理出对于每个位置的上一个满足条件的位置
//对于一个区间[l,r],如果区间内存在a^b=k,则必有对于 l<=i<=r,ans[i]>=l即可
//所以利用ST表或者线段树维护区间最大值即可,还可以用DP思路:

//ST:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+10;
int f[N][25],n,m,k,ans[N],lg[N],a[N];
signed main()
{
    cin>>n>>m>>k;
    for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
    for(int i=1;i<=n;i++){
        int x; cin>>x;
        ans[i]=a[x^k];
        a[x]=i;
    }
    for(int i=1;i<=n;i++) f[i][0]=ans[i];
    for(int j=1;j<=lg[n];j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    while(m--){
        int l,r; cin>>l>>r;
        int pos=lg[r-l+1];
        if(max(f[l][pos],f[r-(1<<pos)+1][pos])>=l) cout<<"yes"<<endl;
        else cout<<"no"<<endl;
    }
    return 0;
}

//DP:
//f[i]表示的i之前的最大的L
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=2e6+10;
int n,m,res,a[N],f[N],ans[N],k,x;
map<int,int>mp;
signed main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        cin>>x;
        f[i]=max(f[i-1],mp[x^k]);
        mp[x]=i;
    }
    while(m--){
        int l,r; cin>>l>>r;
        if(f[r]>=l) cout<<"yes"<<endl;
        else cout<<"no"<<endl;
    }
    return 0;
}
复制代码

 

复制代码
//分冶思想:
//https://codeforces.com/contest/1400/problem/E

//首先考虑全部都竖着涂,很明显只需要涂n次就够了
//所以很明显我们的任务就是要用横着涂 找出涂的次数小于n的方法
//那么有一点要注意,如果我们想要涂第i行,那么i行下面的格子已经是被涂了色的,不然的话我们一列就要涂两次
//很明显多了,也就是我们每次要涂一个区间内,所有共同的最小值,如果我们这样做的代价比全竖着涂的代价小 就用,否则不用

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int dp[5005],n,vis;
int dfs(int l,int r,int high)
{
    if(l==r) return 1;
    int res=0,high_=2e9,pos,ans=r-l+1;
    for(int i=l;i<=r;i++) high_=min(high_,dp[i]);
    res=high_-high;
    for(int i=l;i<=r;i++){
        if(dp[i]==high_) continue;
        for(int j=i;j<=r;j++){
            pos=j;
            if(dp[j+1]==high_) break;
        }
        res+=dfs(i,pos,high_);
        i=pos;
    }
    return min(res,ans);
}
signed main()
{
    cin>>n; 
    //vis 数组用来判断 1 0 这一特殊情况
    for(int i=1;i<=n;i++) cin>>dp[i],vis+=dp[i]; 
    cout<<(vis?0:dfs(1,n,0));
    return 0;
}
复制代码

 

posted @   o-Sakurajimamai-o  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
-- --
点击右上角即可分享
微信分享提示