1. 程式人生 > >[51Nod]NOIP2018提高組省一沖獎班模測訓練(三) 題解

[51Nod]NOIP2018提高組省一沖獎班模測訓練(三) 題解

但是 clas ref 構造 efi const .html 題意 矩形

鏈接

A.Anan的派對

題意:Anan想舉辦一個派對。Anan的朋友總共有 n 人。第i個人如果參加派對會得到 \(c_i\) 的快樂值,除他自己外每多一個人參加他會減少 \(d_i\) 的快樂值。Anan想讓這個派對的總快樂值盡可能大,在這個基礎上,能來的人越多越好。Anan想知道最佳的邀請方案的快樂值與參加人數。對於 \(50\%\) 的數據 , $n\le 1000 $

假設來的人的集合為 \(S\),則快樂值為 \(\sum_{i\in S}c_i-(|S|-1)\times d_i\)

枚舉集合大小,然後排序選前 \(|S|\) 個即可

#include<stdio.h>
#include<algorithm>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
struct node{
    int c,d;
}a[1002];
int n,cnt;
long long ans=-1e18,r;
int main(){
    scanf("%d",&n);
    REP(i,1,n)scanf("%d",&a[i].c);
    REP(i,1,n)scanf("%d",&a[i].d);
    REP(t,1,n){
        std::sort(a+1,a+1+n,[&](const node&a,const node&b){return a.c-1ll*a.d*(t-1)>b.c-1ll*b.d*(t-1);});
        r=0;
        REP(i,1,t)r+=a[i].c-1ll*a[i].d*(t-1);
        if(r>=ans)ans=r,cnt=t;
    }
    printf("%lld\n%d\n",ans,cnt);
    return 0;
}

B.XYG的蛋糕

題意:一個 \(n\times m\) 的矩形,需要把它分成 \(1\times 1\) 的小塊,每次選出已經分出的某一個矩形,切一刀,可以橫切或豎切。求方案數,兩個切法不同當且僅當其中一個切法中存在一條刀痕,在另一個切法中不存在。 \(n,m\le 300\)

考慮DP,設 \(dp[i][j]\) 表示大小為 \(i\times j\) 的矩形的方案數,第一反應是

\(dp[i][j]=\sum_x dp[x][j]\times dp[i-x][j]+\sum_ydp[i][y]\times dp[i][j-y]\)

但是這樣有重復

增加一維 \(k\) 表示允不允許橫切或豎切即可

#include<stdio.h>
#include<cstring>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
const int p=998244353;
int f[305][305][3];
inline int dfs(int x,int y,int z){
    if(x==1&&y==1)return 1;
    int&ans=f[x][y][z];
    if(~ans)return ans;
    ans=0;
    if(z!=1)REP(i,1,x-1)ans=(ans+1ll*dfs(i,y,1)*dfs(x-i,y,0))%p;
    if(z!=2)REP(i,1,y-1)ans=(ans+1ll*dfs(x,i,2)*dfs(x,y-i,0))%p;
    return ans;
}
int main(){
    memset(f,-1,sizeof f);
    int n,m;scanf("%d%d",&n,&m);
    printf("%d\n",dfs(n,m,0));
    return 0;
}

C.WZD的洞洞

題意:平面上有n個物品,每個物品有坐標xi,yi和價值wi,WZD希望用洞洞包含住價值盡可能高的物品。由於洞洞的構造,其兩條邊必須平行於坐標軸,且直角在左下方(也就是說,洞洞只能平行移動),並且要求直角必須恰好落在一個物品上。求洞洞能包含住最大價值為多少的物品。n≤100000,0<xi,yi≤10000,-10000<wi<10000。

這個直角三角形只能平行移動,這意味著它的斜邊的斜率是不變的,而且題目要求直角頂點一定在一個物品上,所以如果用斜率為斜邊的斜率的直線掃,頂點在一個物品上的三角形能包含另外一個物品的條件是他們在這個直線間的距離小於三角形的高,因此two pointers搞一下,樹狀數組維護一下即可

#include<stdio.h>
#include<cmath>
#include<algorithm>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
const int N=1e5+5,S=10005;
struct BIT{
    int c[S],n;
    inline void add(int p,int x){for(;p<=n;p+=p&-p)c[p]+=x;}
    inline int ask(int p){int r=0;for(;p;p-=p&-p)r+=c[p];return r;}
}tx,ty;
int n,A,B;
struct node{int x,y,w;double d;}a[N];
inline void smax(int&x,const int&y){x<y?x=y:0;}
int main(){
    scanf("%d%d%d",&n,&A,&B);
    const double base=sqrt(A*A+B*B),maxd=A*B/base;
    REP(i,1,n){
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
        a[i].d=(double)(B*a[i].x+A*a[i].y)/base;
        smax(tx.n,a[i].x),smax(ty.n,a[i].y);
    }
    std::sort(a+1,a+1+n,[&](const node&a,const node&b){return a.d>b.d;});
    int l=1,sum=0,ans=0;
    REP(r,1,n){
        sum+=a[r].w;
        while(l<r&&a[l].d-a[r].d>maxd){
            tx.add(a[l].x,-a[l].w),ty.add(a[l].y,-a[l].w);
            sum-=a[l++].w;
        }
        tx.add(a[r].x,a[r].w),ty.add(a[r].y,a[r].w);
        smax(ans,sum-tx.ask(a[r].x-1)-ty.ask(a[r].y-1));
    }
    printf("%d\n",ans);
    return 0;
}

[51Nod]NOIP2018提高組省一沖獎班模測訓練(三) 題解