2022.6.15练题

早上七点打开cf,做了昨天的div4,花了一上午才做完并且写完题解。

链接:https://codeforces.com/contest/1692

A
顺序比较即可,O(N)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5;
int a[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        int ans=0;
        for(int i=1;i<=4;i++)    cin>>a[i];
        for(int i=2;i<=4;i++){
            if(a[i]>a[1])    ans++;
        }
        cout<<ans<<"\n";
    }
    return 0;
}

B
S有一个有n个元素的数组a,一步操作包括选两个不同的下标i和j,再将ai和aj移除出数组,当数组里面没有重复数字的时候,输出最终数组的大小
输入
t个样例
n(原数组的长度)
a1~an(数组元素)
输出
最终数组的最大size

用一个set存没有重复的数字,然后模拟样例,可知可分为(n-s.size()%2)==1和==0两种情况,abbbccc,abbbcccc这样列举就得出结论了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
set<int> s;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        s.clear();
        int n;
        cin>>n;
        int x;
        for(int i=0;i<n;i++){
            cin>>x;
            s.insert(x);
        }
        int k=s.size();
        int tem=n-k;
        if(tem%2==1)    cout<<k-1<<"\n";
        else    cout<<k<<"\n";
    }
    return 0;
}

C(象在哪里)
两条"#"的交汇点
O(N^2)暴力即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
char s[9][9];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        int x=0,y=0;
        for(int i=1;i<=8;i++){
            for(int j=1;j<=8;j++){
                cin>>s[i][j];
            }
        }        
        for(int i=2;i<=7;i++){
            for(int j=2;j<=7;j++){
                if(s[i-1][j-1]=='#'&&s[i-1][j+1]=='#'&&s[i+1][j-1]=='#'&&s[i+1][j+1]=='#')    x=i,y=j;    
            }
        }
        cout<<x<<" "<<y<<"\n";
    }
    return 0;
}

D(钟)
HH:MM在加无数次x分钟后,求回文子串的个数

思路:打表得到从00:00开始的回文串的时间(min),然后求HH:MM代表的时间

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
set<string> ss;
set<int> t;
string s;
void init(){
    ss.insert("00:00");//0
    ss.insert("01:10");//70
    ss.insert("02:20");//140
    ss.insert("03:30");//210
    ss.insert("04:40");//280
    ss.insert("05:50");//350
    ss.insert("10:01");//601
    ss.insert("11:11");//671
    ss.insert("12:21");//741
    ss.insert("13:31");//811
    ss.insert("14:41");//881
    ss.insert("15:51");//951
    ss.insert("20:02");//1202
    ss.insert("21:12");//1272
    ss.insert("22:22");//1342
    ss.insert("23:32");//1412
}
int a[5]={600,60,0,10,1};//对应的时间 
int times[16]={70,140,210,280,350,601,671,741,811,881,951,1202,1272,1342,1412};//init()
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    init();
    int test;
    cin>>test;
    while(test--){
        t.clear();
        cin>>s;
        int x;
        cin>>x;
        int fg=0;
        int ans=0;
        for(int i=0;i<5;i++)    fg+=(int)(s[i]-'0')*a[i];
        for(int i=0;i<=10000;i++){
            t.insert(fg);
            fg+=x;
            fg%=1440;//去循环 
        }
        for(auto i:t){
            for(int j=0;j<16;j++){//times.size()
                if(times[j]==i)    ans++;
            }    
        }
        cout<<ans<<"\n";
    }
    return 0;
}

F(读错题目了)
开始以为相加是三位数,实际上是三个不同下标的数加起来位数是3,那么只关心末位即可,223442就相当于是2,然后这样以后v.size()<=30,O(N+30^3)的复杂度,不足1e7,可行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        vector<int> v;
        int digit[10]={};
        for(int i=0;i<n;i++){
            int x;
            cin>>x;
            digit[x%10]++;
        } 
        for(int i=0;i<10;i++){
            for(int j=0;j<min(digit[i],3);j++){
                v.push_back(i);
            }
        }
        int m=v.size();
        bool fg=false;
        for(int i=0;i<m;i++){
            for(int j=i+1;j<m;j++){
                for(int k=j+1;k<m;k++){
                    if((v[i]+v[j]+v[k])%10==3){
                        fg=true;
                        break;
                    }    
                }
            }
        }
        if(fg)    cout<<"YES"<<"\n";
        else    cout<<"NO"<<"\n";
    } 
    return 0;
}

G(2^式的排序)
给一个长度为n的a数组和一个整数k,找到i这个下标(1<=i<=n-k),子数组[ai……ai+k]有以下性质:(子数组长度为k+1)
如果你用第i个元素乘以1<<(i-1),那么子数组就是严格单调递增的。

输入
t个样例
n(数组长度) k(子数组长度-1)
a1~an

输出
下标满足条件的数量

实际上就是判断a[i]和2*a[i+1]的关系,用一个b数组来表示,b[i]==1就说明a[i]<2*a[i+1],b[i]==0就说明a[i]>=2*a[i+1],先算从0~k,然后从k~n-1,注意要减去重复的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+30;
int a[maxn];
int b[maxn]={};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        int n,k;
        cin>>n>>k;
        for(int i=0;i<n;i++)    cin>>a[i];
        for(int i=0;i<n-1;i++){
            b[i]=(a[i]<2*a[i+1]);//只需判断a[i]和2*a[i+1]的关系 
        }
        int tem=0,ans=0;
        for(int i=0;i<k;i++)    tem+=b[i]; //从开始到k 
        if(tem==k)    ans++;
        for(int i=k;i<n-1;i++){
            tem+=b[i];
            tem-=b[i-k];//去掉重复的 
            if(tem==k)    ans++;//满足就ans++; 
        } 
        cout<<ans<<"\n";
    }
    return 0;
} 

E:(二进制双端队列)
S有一个长为n的只有0 1的数组。在一步操作中,他可以移走开头或者最后一个数组元素。问是最小的操作数,使得数组的和为s。

输入
t个样例
n s(数组长度 需要的数组元素和)
a1~an

输出
最小操作步数

先预处理前缀和使得能够O(1)求得a[l]+……a[r],然后再二分,l=i,r=n-1,用一个pos记录下标,ans=min(ans,n-(pos-i+1));还要写一个pd函数,看[l,r]这段数组元素之和与s之间的关系。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1e14+30;
ll pd(int l,int r,vector<ll>& p){//前缀和 
    return p[r]-(l?p[l-1]:0);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        int n,s;
        cin>>n>>s;
        vector<ll> a(n),p(n);
        for(int i=0;i<n;i++){
            cin>>a[i];
            p[i]=a[i];
            if(i)    p[i]+=p[i-1];//前缀和 
        }
        ll ans=inf;
        for(int i=0;i<n;i++){
            int l=i,r=n-1;
            int pos=-1;
            while(l<=r){
                int mid=(l+r)>>1;
                if(pd(i,mid,p)<=s){//l右移 
                    pos=mid;
                    l=mid+1;
                }
                else    r=mid-1;
            }
            if(pos==-1||pd(i,pos,p)!=s)    continue;
            ans=min(ans,(ll)(n-(pos-i+1)));
        }
        if(ans==inf)    cout<<"-1"<<"\n";
        else    cout<<ans<<"\n";
    } 
    return 0;
}

H(赌博)
M在赌场。赌场的游戏是这样的。
在每一轮赌博以前,玩家选择一个在1到1e9之间的数字。在那以后,一个有1e9个面的骰子被扔出,出现了一个从1到1e9的随机数。如果这个玩家猜对了的话他们的钱就会加倍,否则会减半。
M预测未来,知道接下来n轮骰子会转向那个数字。他会挑选三个数字(a,l,r)。他会玩r-l+1轮。在这些轮中的每一轮,他会猜相同的数字a。在l轮之前他有1美元。
M问你来决定a,l,r,使得他在最后赚最多的钱。

输入
t个样例
n(游戏轮数)
x1~xn

实际上就是选择一个区间[l,r]使得里面(一样的数的个数-不一样的数的个数)max。

黑科技:map<int,vector<int>> p;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
    int n;
    cin>>n;
    map<int,vector<int>> p;
    vector<int> x(n);
    for(int i=0;i<n;i++){
        cin>>x[i];
        p[x[i]].push_back(i);
    }
    int a=x[0],l=0,r=1,ans=1;
    for(auto [b,q] : p){
        int min=0,k=q[0];//min左端点  
        for(int j=0;j<int(q.size());j++) {
            int i=q[j];
            int cur=j-(i-j);
            if(cur<min) {
                min=cur;
                k=i;
            }
            int res=cur+1-min;//区间长度 
            if(res>ans) {
                ans=res;
                a=b;
                l=k;
                r=i+1;
            }
        }
    }
    cout<<a<<" "<<l+1<<" "<<r<<"\n";
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

下午准备做一套gym,结果睡了三个小时…………

20:00-0:30   2020-2021 ICPC - Gran Premio de Mexico - Repechaje 

链接:https://codeforces.com/gym/102966

A A的棋盘(数学推公式)
A总是想办法享受乐趣。她把2*2方格中有两个白色两个黑色块叫做P石材配置。要保证每个2∗2的矩阵都有两个黑石头和两个白石头,问有多少种摆放方式?

公式:(1<<m)+(1<<n)-2

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    ll n,m;
    cin>>n>>m;
    //cout<<2*(n*m-1)<<"\n";
    if(n<2||m<2){
        cout<<"0"<<"\n";
    }
    else    cout<<(1ll<<m)+(1ll<<n)-2<<"\n";
    return 0;
}

L(让我们来数F)(数论题,分解质因数)
有多少与A、B不同的素数被乘在一起来获得A*B?

就是求 A , B的共同质因数

对于共同质因数的标记,使用 unordered_map 就可以了,主要的还是欧拉筛的知识。
注意maxn的范围<sqrt(1e7),开始用的1e4 TLE*6

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3200;
inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
int cnt;
int vis[maxn],pri[maxn];
void Euler(int n){//Oula
    for(int i=2;i<=n;i++) {
        if(!vis[i])    pri[++cnt]=i;
        for (int j=1;j<=cnt;j++) {
            if(i*pri[j]>n)    break;
            vis[i*pri[j]]=true;
            if(i%pri[j]==0)    break;
        }
    }
}
unordered_map<int, bool> visn;
void cal(int x){
    for(int i=1;i<=cnt;i++){
        if(x%pri[i]==0){
            visn[pri[i]]=1;
            while(x%pri[i]==0)    x/=pri[i];
        }
    }
    if(x>1)    visn[x]=1;
}
int main(){
    int t=(int)read();
    Euler(maxn);
    while(t--){
        int a=(int)read();
        int b=(int)read();
        visn.clear();
        cal(a);
        cal(b);
        printf("%d\n",visn.size());
    }
    return 0;
}

K(厨余垃圾)(英语阅读理解题)

有n个面包,m口锅,每口锅中都装了一定体积的汤,要将汤淋在面包上,面包也有体积,一体积汤能淋一体积面包。一次淋面包的操作必须要将整个面包都淋上汤,若锅中剩余汤的体积不足以将整个面包都淋上,则将这个锅中的汤都倒掉。

O(N)简单模拟,难点在于看懂题目

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+30;
int a[maxn],b[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n,m;
    cin>>n>>m;
    ll ans=0;
    for(int i=1;i<=n;i++)    cin>>a[i];
    for(int i=1;i<=m;i++)    cin>>b[i];
    for(int i=1;i<=n;i++){
        if(b[i]<a[i])    continue;
        else if(b[i]>=a[i])    ans+=(b[i]-a[i]);
    }
    if(n==m)    cout<<ans<<"\n";
    else if(m>n){
        for(int i=n+1;i<=m;i++)    ans+=b[i];
        cout<<ans<<"\n";
    }
    return 0;
}

G(G碰撞)(规律题)
G有以下行为:
1.最初,他们指在一个方向(左:0 右:1)
2.没有障碍时,他们在一个方向继续前进
3.如果两个G相撞,他们会立刻改变他们的方向
4.当G达到平台的末端,它就输了

结论:无论怎么撞,最终都会有一辆车子走的是他自己的路程或者别人的路程,取向右的最大和向左的最大即可。
即 if(y) ans=max(ans,l-x);
else ans=max(ans,x);

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    ll l,n;
    cin>>l>>n;
    ll ans=0;
    for(int i=1;i<=n;i++){
        ll x,y;
        cin>>x>>y;
        if(y)    ans=max(ans,l-x);
        else    ans=max(ans,x);
    }
    cout<<ans<<"\n";
    return 0;
}

C(CLETS巡逻队)(组合数学)
CLETS正在研究一种新的方法来组织他们的追随者的巡逻,这样他们就不可能预测某个英俊的英国间谍会潜入他们的基地。为此,当一个追随者到达一个岗位时,计算机将随机选择该追随者下一个岗位。通过一些分类手段,我们获得了计算机用来选择下一步将追随者送往何处的概率分布。你的任务是利用这些信息来确定每一个岗位上都有一名警卫在M步之后到达那里的可能性,这样间谍就可以规划到CLETS的最安全路线。
一个步骤被定义为从一个岗位移动到另一个岗位,或者在同一岗位上等待,如果计算机决定这样做的话。卫兵在第一根柱子上开始轮班。

输入
我们感兴趣的岗位数量和步骤数量。
一个M*M的矩阵

概率跳过,不知道怎么算概率的,样例是什么意思…………

H(H训练)(数学,推理公式)
棉花糖M收养了一群超级聪明的仓鼠。由于M是认真负责的,他无聊的时候就训练他的仓鼠朋友。训练的规则如下:
1.M分了N^2的纸牌(从1~N^2)
2.M有一个口哨来提醒仓鼠是时候该走了。在那个时刻,仓鼠们各自拿起一张牌,用这N张纸牌组成
一条线。
3.这条线可能是被认为失败或者成功的,但是动物们都很聪明,永远不会输。
4.要取得成功,该行中的每张牌上的数字都必须大于或等于左侧的所有数字。
5.完成并评估线路后,仓鼠将卡片交回,并为下一轮做好准备。
为了估计可能需要的时间,他想知道有多少有效配置。

即给一个n,从1~n中选出n个数(可重复选)组成长度为n的非降序数组。

猜公式C(n,2*n-1)

证明:

 

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=2e5+30;
const ll mod=1e9+7;
ll fa[maxn],fb[maxn];
ll quickpow(ll a,ll b){//快速幂
    ll ans=1;
    while(b){
        if(b&1)    ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
ll Cal(ll n,ll k){
    if(n<0||k<0||k>n)    return 0;
    return fa[n]*fb[k]%mod*fb[n-k]%mod;
}
void init(){
    fa[0]=1;
    fb[0]=1;
    for(int i=1;i<=maxn-1;i++){
        fa[i]=fa[i-1]*i%mod;
        fb[i]=quickpow(fa[i],mod-2)%mod;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    //C(n,2*n-1)
    int t;
    cin>>t;
    init();
    while(t--){
        ll n;
        cin>>n;
        cout<<Cal(2*n-1,n)<<"\n";
    }
    return 0;
}

F(数学,推公式,奥数)
存在一个如上图所示的有五个房间的房子,每个房子里有n * n个格子,
求该图中所有格子对之间的曼哈顿距离总和。

推公式

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int main(){
    ll n;
    while(scanf("%lld",&n)!=EOF){
        ll ans=((n-1)*n*n/2-(n-1)*n%mod*(2*n-1)/6)%mod;
        ll a=(2*n*n*ans)%mod;
        ll b=((n*n%mod*n%mod*n%mod*(n+1)/2)%mod+(n*(2*ans%mod*n%mod+n*n*n*(n-1)/2))%mod)%mod;
        ll c=(b+(n*n)%mod*(n*n)%mod*n)%mod;
        ll d=(((n*n)%mod*(n*n)%mod*(n+1))%mod+((n-1)*(n*n)%mod*(n*n)%mod)%mod)%mod;
        ll anss=((a*5)%mod+b*4%mod+c*2%mod+d*4%mod)%mod;//乘以对应的数量
        printf("%lld\n",anss);
    }
    return 0;
}

还有一道质因数分解的D,明天再补(已经00:49了)(QAQ)

posted @ 2022-06-16 00:50  cf不上1500不改名  阅读(26)  评论(0)    收藏  举报