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

Posted 斗奋力努

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;

以上是关于Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)(ABCDE)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #775 (Div. 2) ABCD题解

Codeforces Round #775 (Div. 2) ABCD题解

Codeforces Round #775 (Div. 2) ABCD题解

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

Codeforces Round #774 (Div. 2)(ABCDE)

Codeforces Round #436 E. Fire(背包dp+输出路径)