codeforce Hello 2020 A~E

题目链接:http://codeforces.com/contest/1284

 

A.New Year and Naming

给数字,求s字符串数组中对应命名+t字符串数组中对应命名,取余即可

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m;
    string s[30],t[30];
    cin>>n>>m;
    for(int i=0;i<n;i++)cin>>s[i];
    for(int i=0;i<m;i++)cin>>t[i];
    int q;
    cin>>q;
    while(q--)
    {
        int y;
        cin>>y;
        cout<<s[(y-1)%n]<<t[(y-1)%m]<<endl;
    }
    return 0;
} 

 

B.New Year and Ascent Sequence

给n组数,每组数先说明有多少个数,再依次给出,n组数可以左右相接,构成组成n*n组数

如果数组a[]里存在i<j且a[i]<a[j],则符合要求,计算这n*n组里共有多少组数符合要求。

思路:一组数可以和自己拼接,也可以拼在别人前面/后面。

如果它本身就含有升序,则无论怎么拼都符合要求,这一类数放一堆,每组对答案的贡献应为n*2-1,因为自己与自己相组不分前后。

但是这样重复计算了贡献,故应改成(n-cnt)*2-1,cnt为截止到第i个时,本身含有升序的组数。

重复计算贡献样例

input:
3
2 0 2
2 0 2
2 0 2
output:
9

如果本身不含有升序(即全递减),记录数组里最大值H[i]、最小值L[i],这另一类数放一堆。

在这堆里,每组数对答案的贡献为,该堆中H[i]大于L[该组i]的数量。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int s[1000050];
int flag[maxn],h[maxn],l[maxn];
vector<int> high,low;
int main()
{
    long long ans=0;
    int n,cnt=0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int len;
        cin>>len;
        int tmph=0,tmpl=1e6;
        for(int j=0;j<len;j++)
        {
            scanf("%d",&s[j]);
            if(j!=0&&s[j]>s[j-1])flag[i]=1;
            tmph=max(tmph,s[j]);
            tmpl=min(tmpl,s[j]);
        }
        if(!flag[i])
        {
            h[i]=tmph;
            l[i]=tmpl;
            high.push_back(tmph);
            low.push_back(tmpl);
        }
    } 
    sort(high.begin(),high.end());
    sort(low.begin(),low.end());
    int size=high.size();
    for(int i=0;i<n;i++)
    {
        if(flag[i]==1)ans+=((n-cnt)*2-1),cnt++;
        else
        {
            int num=upper_bound(high.begin(),high.end(),l[i])-high.begin();
            ans+=size-num;
        }
    }
    cout<<ans<<endl;
    return 0;
} 

 

C.New Year and Permutation

(先放个人思路,下有正解)

n的全排列(n!种情况)里找,每种情况里找符合以下条件的所有i,j的组数,答案为所有情况组数之和

从a[i]到a[j]里,最大值max-最小值min=j-i

暴力枚举答案:1,6,32,180,1116,7728,59904,518400

枚举从3推到4的情况才发现规律,按理应该加上n=3时的r的,不过发现系数有规律就直接*(n+1)/2了

#include<bits/stdc++.h>
using namespace std;
int jie[250050];
int extgcd(int a,int b,int& x,int& y)
{
    int d=a;
    if(b!=0)
    {
        d=extgcd(b,a%b,y,x);
        y-=(a/b)*x;
    }
    else 
    {
        x=1;
        y=0;
    }
    return d;
}
int mod_inverse(int a,int m)
{
    int x,y;
    extgcd(a,m,x,y);
    return(m+x%m)%m;
}
int main()
{
    int n,m;
    long long r=0;
    cin>>n>>m;
    jie[1]=1;
    for(int i=2;i<=n;i++)
    {
        jie[i]=1ll*jie[i-1]*i%m;
    }
    for(int i=1;i<=n;i++)
    {
        r=(r+1ll*jie[i]*jie[n-i+1]%m)%m;
    }
    
    //*1,*1.5,*2,*2.5,*3,*3.5......
    //2*1=1,4*1.5=6,16*2=32,72*2.5=180
    r=r*(n+1)%m;
    r=r*mod_inverse(2,m)%m;//除以2等于乘上逆元 
    cout<<r<<endl;
    return 0;
} 

标程很简单啊emm......考虑[l, r]组合的贡献,l - r 在每个项里都有出现,当作整体来看

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 250005;
using lint = long long;
 
 
using pi = pair<int, int>;
 
int n, mod;
lint fact[MAXN];
 
int main(){
    fact[0] = 1;
    cin >> n >> mod;
    for(int i=1; i<=n; i++) fact[i] = fact[i-1] * i % mod;
    lint ret = 0;
    for(int i=1; i<=n; i++){
        ret += (n - i + 1) * (fact[i] * fact[n - i + 1] % mod);
        ret %= mod;
    }
    cout << ret << endl;
}

 

D. New Year and Conference

题意&思路:https://blog.csdn.net/sigh_/article/details/103838790

个人代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int inf=1e9+10;
struct Node
{
    int sa,ea,sb,eb;
    Node(){}
    Node(int sa_,int ea_,int sb_,int eb_):sa(sa_),ea(ea_),sb(sb_),eb(eb_){}
    bool operator<(const Node &rhs)
    {
        if(sa==rhs.sa)return ea<rhs.ea;
        return sa<rhs.sa;
    }
}P[maxn];//要用这句话,又定义了构造函数,就得加Node(){}

int smax[maxn<<2],smin[maxn<<2];
void up(int p)
{
    smax[p]=max(smax[p<<1],smax[p<<1|1]);
    smin[p]=min(smin[p<<1],smin[p<<1|1]);
}
void build(int p,int l,int r)
{
    if(l==r)
    {
        smax[p]=P[l].sb;
        smin[p]=P[l].eb;
        return;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    up(p);
}
int querymin(int p,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return smin[p];
    int mid=l+r>>1;
    int res=inf;
    if(x<=mid)res=min(res,querymin(p<<1,l,mid,x,y));
    if(y>mid)res=min(res,querymin(p<<1|1,mid+1,r,x,y));
    return res;
}
int querymax(int p,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return smax[p];
    int mid=l+r>>1;
    int res=-inf;
    if(x<=mid)res=max(res,querymax(p<<1,l,mid,x,y));
    if(y>mid)res=max(res,querymax(p<<1|1,mid+1,r,x,y));
    return res;
}
bool solve(int n)
{
    sort(P+1,P+1+n);
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(P+1,P+1+n,Node(P[i].ea,inf,0,0))-P-1; //pos∈[0,n],对应P[pos+1] 
        if(!(i+1<=pos))continue;//a场地无冲突 
        
        //a场地与[i+1,pos]都冲突 
        //b场地也需要冲突,如果b场地[i+1,pos]里有一个不冲突即为false
        //最小的eb比P[i].sb小,或者最大的sb比P[i].eb大 
        if(querymin(1,1,n,i+1,pos)<P[i].sb||querymax(1,1,n,i+1,pos)>P[i].eb)
            return false;
    }
    return true;
}
int main()
{
    int n,sig=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&P[i].sa,&P[i].ea,&P[i].sb,&P[i].eb);
    }
    sig&=solve(n);
    for(int i=1;i<=n;i++)
    {
        swap(P[i].sa,P[i].sb);
        swap(P[i].ea,P[i].eb);
    }
    sig&=solve(n);
    if(sig)printf("YES\n");
    else printf("NO\n");
    return 0;
}

 

E.New Year and Castle Construction

给n个点,问存在多少个四个点围成的四边形(可能是凹四边形,这样围成的就是三角形)严格包围某个点。

不存在三点共线,支持复杂度O(n*n*logn)。

所有情况数减去不能围成四边形的组数,即为答案。

参考:https://blog.csdn.net/qq_43326267/article/details/103845048

把弧度制转成角度值放进vector会wa15,盲猜还是精度问题

#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
long double x[maxn],y[maxn],pi=acos(-1.0L);  //不开long double有精度问题 
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)cin>>x[i]>>y[i];
    long long res=1ll*n*(n-1)*(n-2)*(n-3)*(n-4)/24;//n个点选四个共有res种情况 
    for(int i=0;i<n;i++)//完全围住点i 
    {
        vector<long double> v;
        for(int j=0;j<n;j++)
        {
            if(i==j)continue;
            v.push_back(atan2(y[j]-y[i],x[j]-x[i]));//求y相对于x的角度
        }
        sort(v.begin(),v.end());
        
        int size=n-1,index=0;
        for(int j=0;j<size;j++)//枚举另一个点 
        { 
            while(index<j+size)
            {
                long double ang=v[index%size]-v[j];//index点相对于j点的实际角度,非升序,所以需要环形扫一遍 
                if(ang<0)ang+=2*pi;
                if(ang<pi)index++; 
                else break;//在点i、j构成的线下方则break 
            }
            
            //index是从相对于x的角度-180开始枚举到+180,枚举两轮
            //枚举到相对于j的角度大于等于180为止,共有index-j个点,不包括j,故再-1 
            long long cnt=index-j-1;
            //点i、j构成的线(以该线为x轴)上方共有cnt个点,cnt个点随便取三个,与i,j点组合的情况都要减去 
            res-=1ll*cnt*(cnt-1)*(cnt-2)/6;
        }
    }
    cout<<res<<endl;
    return 0;
} 
posted @ 2020-01-04 23:27  myrtle  阅读(318)  评论(0编辑  收藏  举报