1. 程式人生 > 實用技巧 >The 13th Chinese Northeast Collegiate Programming Contest

The 13th Chinese Northeast Collegiate Programming Contest

題目列表


B - Balanced Diet

題意: 給你m種食物,給你1~m個L[i]表示如果你要取第i種食物,你至少要取L[i]個。再給你n個食物,有n個a[i]和b[i]分別表示食物的價值和種類,設U為食物的總價值,V為所有種類的食物中數量最大的種類的數量,問U/V的最大值是多少。
思路:

首先我們看取某種食物的順序,因為U越大越好,因此每類食物都要從大到小排序,然後列舉每個分母V的值,那麼分母U的值也就是在符合L[i]的前提下取第i種食物的前V項即可。
程式碼:

‘’‘’‘’
const int maxn = 100005;
inline ll gcd(ll a,ll b) {
    return b>0 ? gcd(b,a%b):a;
}
struct node{
    int l,id;
}ss[maxn];
int n,m;
vector<int>swt[maxn];
vector<int>sum[maxn];
bool cmp(int x,int y)
{
    return x>y;
}
int main()
{
    int T;
    cin>>T;
    while(T--){
        scanf("%d %d",&n,&m);
        for(int i=1;i<=max(n,m);i++){
            swt[i].clear();
            sum[i].clear();
        }
        for(int i=1;i<=m;i++){
            scanf("%d",&ss[i].l);
            ss[i].id=i;
        }
        for(int i=1;i<=n;i++){
            int x,y;
            scanf("%d %d",&x,&y);
            swt[y].push_back(x);
        }
        for(int i=1;i<=m;i++){
            sort(swt[i].begin(),swt[i].end(),cmp);
      for(int j=0;j<swt[i].size();j++){
                sum[max(j+1,ss[i].l)].push_back(swt[i][j]);
            }
        }
        ll mm=0,zz=0,nm=1,nz=0;
        for(int i=1;i<=n;i++){
            mm=i;
        for(int j=0;j<sum[i].size();j++){
                zz+=sum[i][j];
          }
            f(1ll*nm*zz>1ll*nz*mm){
                nm=mm;
                nz=zz;
            }
        }
        ll pp=gcd(nz,nm);
      printf("%lld/%lld\n",nz/pp,nm/pp);
    }
    return 0;
 }


C - Line-line Intersection

思路:兩條直線不平行則必相交,若平行:若相交那麼答案+1,否則不算。兩個map去儲存,第一個map儲存某一斜率下平行的數量,第二個map儲存某一條直線的重合數量。在新增第i條直線時,答案ans=i-平行數量+重合數量。
程式碼:

‘’‘’‘’
map<pair<ll,ll>,ll>k;
map<pair<pair<ll,ll>,ll>,ll>c;
int main()
{
    int n,t;
    t=read();
    while(t--)
    {
        n=read();
        ll ans=0;
        k.clear();
        c.clear();
        for(int i=0;i<n;i++)
        {
            ll x1,y1,x2,y2,x;
            scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
            x=x1*y2-x2*y1;
            ll xx=x2-x1;
            ll yy=y2-y1;
            ll d=__gcd(xx,yy);
            xx/=d;
            yy/=d;
            x/=d;
            ans+=i-k[{xx,yy}]+c[{{xx,yy},x}];
            k[{xx,yy}]++;
            c[{{xx,yy},x}]++;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

E - Minimum Spanning Tree

題意:給你一幅圖,把邊看成點,有公共點的兩條邊就是有一條連邊的兩個點,邊權就是兩條邊的邊權之和,讓你構造一顆最小生成樹,問你邊權之和是多少。
思路:我們從每條邊的貢獻入手,顯然,一個點的所有出邊中,我們要用到最多的就是最小邊,最小邊要和可以和它相連的所有邊相連,則其貢獻就是(出邊數-1)*w,對於其它邊,要保證連通,都會用到一次。
程式碼:

vector<ll>g[maxn];
int main()
{
    ll T,n;
    cin>>T;
    while(T--){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            g[i].clear();
        }
        for(int i=1;i<n;i++){
            ll u,v,w;
            scanf("%lld %lld %lld",&u,&v,&w);
            g[u].push_back(w);
            g[v].push_back(w);
        }
        ll ans=0;
        ll res=0;
        ll minn=INF;
        for(int i=1;i<=n;i++){
            res=0;
            minn=INF;
            if(g[i].size()<=1)continue;
            for(int j=0;j<g[i].size();j++){
                ll u=g[i][j];
                res+=u;
                minn=min(minn,u);
            }
            res+=(g[i].size()-2)*minn;
            ans+=res;
        }
        printf("%lld\n",ans);
    }
    return 0;
 }

G - Radar Scanner

題意:在二維平面上,給你n個矩形的左下角和右上角座標,一次移動可以選擇一個矩形向上下左右移動一格。問你最少多少次移動可以讓所有矩形有一個公共相交點。
思路:首先是二維平面上的問題,且相互獨立,那麼可以轉化為一維問題。就是若干條直線,最少移動多少次有公共點。猜一下是移到所有點的中位數就是最小。
程式碼:

‘’‘’‘’
const int maxn = 100005;
vector<ll>g[maxn];
int main()
{
    ll T,n;
    cin>>T;
    while(T--){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            g[i].clear();
        }
        for(int i=1;i<n;i++){
            ll u,v,w;
            scanf("%lld %lld %lld",&u,&v,&w);
            g[u].push_back(w);
            g[v].push_back(w);
        }
        ll ans=0;
        ll res=0;
        ll minn=INF;
        for(int i=1;i<=n;i++){
            res=0;
            minn=INF;
            if(g[i].size()<=1)continue;
            for(int j=0;j<g[i].size();j++){
                ll u=g[i][j];
                res+=u;
                minn=min(minn,u);
            }
            res+=(g[i].size()-2)*minn;
            ans+=res;
        }
        printf("%lld\n",ans);
    }
    return 0;
 }


H - Skyscraper

題意:n座摩天大樓,每座的預計層高為a[i]。
m次操作:
1、將a的某個區間[l , r]加上k。
2、詢問僅對[l, r]區間從0開始施工,最少需 要幾個階段。
每一個階段操作,可以任意選取一個區間,在這個區間上每個a[i]加上1 。
思路:
** 1、首先令b[I]=a[I]-a[i-1] 。
** 2、b[i]>0,那麼要多花b[i]個階段才能把a[i]修好。
** 3、b[i]<=0,那麼在修a[i-1]時就可以把a[i]順帶著修好。
** 4、那麼令c[I]=b[i] (b[ I]>0),就表示在修好i-1這個樓後要修i這個樓需要的階段。
** 5、那麼對於區間[l,r]需要的階段就是a[l]+c[l+1]+c[l+2]+……+c[r-1]+c[r]。
** 6、也就是(b[1]+b[2]+……+b[l])+(c[l+1]+c[l+2]+……+c[r-1]+c[r])。
** 7、對於區間加的操作,我們只需要在b和c陣列的l處加上k,r+1處減去k,即可。
** 8、用樹狀陣列維護字首和即可。
程式碼:

‘’‘’‘’
const int maxn = 100005;

ll a[maxn],b[maxn],c[maxn];
ll T,n,m;
struct tr{
    ll sum[maxn];
    void add(int p,ll x){
        while(p<=n){
            sum[p]+=x;
            p+=p&(-p);
        }
    }
    ll ask(int p){
        ll res=0;
        while(p){
            res+=sum[p];
            p-=p&(-p);
        }
        return res;
    }
    ll r_ask(int l,int r){
        return ask(r)-ask(l-1);
    }
}tb,tc;
int main()
{
    cin>>T;
    while(T--){
        for(int i=1;i<maxn;i++){
            tb.sum[i]=0;
            tc.sum[i]=0;
            b[i]=0;
        }
        scanf("%lld %lld",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            if(i>=2){
                b[i]=a[i]-a[i-1];
                tb.add(i, b[i]);
                if(b[i]>0){
                    tc.add(i, b[i]);
                }
            }else{
                b[i]=a[i];
                tb.add(i, a[i]);
                tc.add(i, a[i]);
            }
        }
        int op,l,r,x;
        while(m--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d %d %d",&l,&r,&x);
                tb.add(l, x);
                tb.add(r+1,-x);
                if(b[l]>0){
                    tc.add(l, x);
                }else if(b[l]+x>0){
                    tc.add(l, b[l]+x);
                }
                if(b[r+1]>0){
                    if(b[r+1]-x>0){
                        tc.add(r+1, -x);
                    }else{
                        tc.add(r+1, -b[r+1]);
                    }
                }
                b[l]+=x;
                b[r+1]-=x;
            }else{
                scanf("%d %d",&l,&r);
                printf("%lld\n",tb.r_ask(1,l)+tc.r_ask(l+1,r));
            }
        }
    }
    return 0;
 }

J - Time Limit

程式碼:

‘’‘’‘’
#include<bits/stdc++.h>
using namespace std;
int t,a[1001],n;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int k=3*a[1];
       
        for(int i=2;i<=n;i++)
        {
           k=max(k,1+a[i]);
        }
        if(k%2==0) printf("%d\n",k);
        else printf("%d\n",k+1);
    }
    return 0;
}

記 2020 9.5 組隊訓練