Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)(ABCDE)

Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)(ABCDE)

总结:
E题初始考虑不全面,组合数字不太行,忘记了多重集全排列的公式,还是找翻得以前的板子。
同时今天发现竟然不会求三角形面积,寄。

A. Game
在这里插入图片描述
题意:给定长度为n的序列,初始在位置1,目标位置n,只能移动到元素为1的位置,免费在相邻位置之间跳跃,之外每次跳跃花费为跳跃前后两点的距离差,问最少花费多少,可以到达位置n,保证位置1和位置n的元素为1
思路:找到初始位置1向右第一个0的位置pos1,和位置n向左第一个0的位置pos2,可以直达就是0,否则就是pos2-pos1+2

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,q,a[N];

void solve(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int l=-1,r=-1;
    for(int i=1;i<=n;i++){
        if(a[i]==1) continue;
        else {l=i;break;}
    }
    for(int i=n;i>=1;i--){
        if(a[i]==1) continue;
        else {r=i;break;}
    }
    if(l==-1) puts("0");
    else printf("%d\n",r-l+2);
}

int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
    return 0;
}

B. Game of Ball Passing
在这里插入图片描述
题意:长度为n的序列,第i个元素代表第i名球员的传球次数,问最少用了几个球进行传球训练,没有人传球后就换新球????(具体看样例,解释不清)
思路:
如果总的传球次数为0,那么就没有用球;
如果有一名球员的传球次数x大于总传球次数sum的一半,那么他开始传球,可以进行x-(sum-x)次;
否则只需要1次传球

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll n,m,q,a[N];

void solve(){
    ll sum=0;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sum+=a[i];
    }
    sort(a+1,a+n+1);
    if(sum==0)  puts("0");
    else if(a[n]*2>sum) printf("%lld\n",a[n]-(sum-a[n]));
    else puts("1");
}

int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
    return 0;
}

C. Weird Sum
在这里插入图片描述
题意:给定一个n*m的网格,按照元素大小进行分类,每类中两两组对,求所有元素的所有对的曼哈顿距离的总和
思路:我们发现数据很大,我们对元素分好类后,实际上某元素的曼哈顿距离和就是里面任意的x对和任意的y对之和,所以我们贪心的将x和y进行从小到大排序,进行求值累加。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+5;
ll n,m,q;
vector<ll>x[N],y[N];

void solve(){
    for(ll i=0;i<N;i++) x[i].clear(),y[i].clear();
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=m;j++){
            ll nu; scanf("%lld",&nu);
            x[nu].push_back(i);
            y[nu].push_back(j);
        }
    }
    ll sum=0;
    for(ll i=1;i<=100000;i++){
        sort(x[i].begin(),x[i].end());
        sort(y[i].begin(),y[i].end());
        ll num1=0,num2=0,len=x[i].size();
        for(ll j=1;j<len;j++){
            num1+=j*(x[i][j]-x[i][j-1]);
            num2+=j*(y[i][j]-y[i][j-1]);
            sum+=num1+num2;
        }
    }
    printf("%lld\n",sum);
}

int main(){
    int t=1;
    while(t--) solve();
    return 0;
}

D. Integral Array
在这里插入图片描述
题意:给定长度为n的序列a,对于所有满足x>=y的对,得到值x/y。问所有的值是否刚好等于序列a中出现的元素
思路:我们知道 x / x = 1 x/x=1 x/x=1,所以如果序列a中没有出现元素1,那么肯定会得到一个序列外的元素1,那么就输出No
(同时我们也知道,相同元素出现的没用的,好像也没影响)
此时,在知道了存在元素1的情况下,我们遍历一下可能出现的值 [ 2 , c ] [2,c] [2,c]。如果当前元素 i i i没有出现,那么我们将看是否存在一对数,满足 大 数 / 小 数 = i 大数/小数=i /=i,有就输出No;最后遍历结束没有就输出Yes

除以一个数等于i
l			r              r/l
i*1      i*1+(i-1)    	    i
i*2      i*2+(i-1)          i
...      ...               ...
i*x      i*x+(i-1)          i
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m,q,c,a[N],num[N],pre[N];
    
void solve(){
    scanf("%d%d",&n,&c);
    for(int i=1;i<=c;i++)pre[i]=num[i]=0;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),num[a[i]]++;
    for(int i=1;i<=c;i++) pre[i]=pre[i-1]+num[i];
    if(num[1]==0){puts("No");return;}
    for(int i=2;i<=c;i++){
        if(num[i]!=0){
            for(int j=1;;j++){
                int l=i*j,r=i*j+i-1;
                if(l>c) break;
                r=min(r,c);
                int hv=pre[r]-pre[l-1];
                if(hv!=0&&num[j]==0){puts("No");return;}
            }
        }
    }
    puts("Yes");
}
    
int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
    return 0;
}

E. Tyler and Strings
在这里插入图片描述注:写的血压上来的题
题意:给出一个长度为n的序列s和一个长度为m的序列t,可以打乱序列s的顺序,问有多少种方式使得s的字典序小于t
思路:首先说明一下,长度相同,但所有元素都不同和有相同元素的两种序列的全排列的个数是不同的。知道这个就很好做本题了。

我们知道序列s的字典序要小于序列t。
对于位置 i i i来说,
序列s可以等于序列t, 即 ( s [ i ] = t [ i ] ) 即(s[i]=t[i]) (s[i]=t[i]),那么继续,同时序列s可用元素 t [ i ] t[i] t[i]减少一个;
或者序列s小于序列t, 即 ( s [ i ] < t [ i ] ) 即(s[i]<t[i]) (s[i]<t[i]),那么后面序列s的排序方式任意,对答案的贡献也就是小于 t [ i ] 的 个 数 m i _ n u m ∗ 序 列 s 剩 余 的 元 素 的 多 重 集 全 排 列 t[i]的个数mi\_num*序列s剩余的元素的多重集全排列 t[i]mi_nums
为了解决求多重集全排列时的除操作,我们使用逆元,先求出初始的多重集全排列分母的逆元di,随后计算中我们使用元素num,那么di*当前元素num的个数就是在前i位都相同的情况下,此时多重集全排列分母的逆元di。
记得如果序列s是序列t的前缀且不相等也是一种情况

至于得到小于某个数的元素个数,因为这里在相等的情况下需要减去元素,所以用个树状数组来记录元素个数就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+5;
const ll mod=998244353;
ll n,m,s[N],t[N];
ll num[N],tr[N],A[N],sum;

ll ksm(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

ll lowbit(ll x){return x&(-x);}

void add(ll x,ll val){
    while(x<N){
        tr[x]+=val;
        x+=lowbit(x);
    }
}

ll query(ll x){
    ll ans=0;
    while(x){
        ans+=tr[x];
        x-=lowbit(x);
    }
    return ans;
}

void solve(){
    A[0]=1;
    for(ll i=1;i<=200000;i++) A[i]=(A[i-1]*i)%mod;
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++) scanf("%lld",&s[i]),num[s[i]]++,add(s[i],1);
    for(ll i=1;i<=m;i++) scanf("%lld",&t[i]);
    ll di=1;
    for(ll i=1;i<=200000;i++){
        if(num[i]) di=(di*A[num[i]])%mod;
    }
    di=ksm(di,mod-2);
    for(ll i=1;i<=m&&i<=n+1;i++){
        if(i==n+1) {sum++;break;}
        ll mi_num=query(t[i]-1);
        sum=(sum+mi_num*A[n-i]%mod*di)%mod;
        if(num[t[i]]){
            di=(di*num[t[i]])%mod;
            add(t[i],-1);
            num[t[i]]--;
        }
        else break;
    }
    printf("%lld\n",sum%mod);
}

int main(){
    int T=1;
    while(T--) solve();
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值