1. 程式人生 > >3.14雜題選講

3.14雜題選講

abs 長度 push_back https ceil push class getchar com

強烈譴責這次的出題人把難度搞這麽高,讓我這個菜雞怎麽活……


T1

非常顯然的思路是01分數規劃,然後就不會做了(大霧

考慮\(a\)小的可以連接到\(a\)大的任務上一起做,那麽顯然也可以把任務按\(a\)排序後相同的放在一起分組。

然後就可以開心快樂地DP了。

\(dp_{i,j}\)表示前\(i\)組裏有\(j\)個記入答案,所能得到的最小權值(權值就是01分數規劃搞出來的東西)。

於是有轉移方程:
\[ dp_{i,j}=\max\{dp_{i-1,j-k}+W_{i,k}\} \]
其中\(W_{i,k}\)表示第\(i\)組裏權值最小的\(k\)個的權值之和,隨便處理一下就好了。

註意一個狀態要合法需要滿足一些條件,可以自己推一下。

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 100
    typedef long long ll;
    typedef long double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char sr[1<<21],z[20];int C=-1,Z=0;
    inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    inline void print(register int x)
    {
        if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++C]=z[Z],--Z);sr[++C]='\n';
    }
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n;
struct hh{ll a,b;inline bool operator < (const hh &y) const {return a>y.a;}}a[sz];

vector<db>w[sz],sum[sz];
int c[sz];
db dp[sz][sz]; // ?°i×é£?óDj??óDD§μ?£?μ?μ?μ?×?′óè¨?μ 
// j*2 \geq c[i]

bool judge(db W)
{
    int cnt=0;
    rep(i,1,n) w[i].resize(0),sum[i].resize(0);
    rep(i,1,n) 
    {
        if (i==1||a[i].a!=a[i-1].a) ++cnt;
        w[cnt].push_back(a[i].a-W*a[i].b);
    }
    rep(i,1,cnt)
    {
        sort(w[i].begin(),w[i].end());
        sum[i].resize(w[i].size());
        sum[i][0]=w[i][0];
        c[i]=c[i-1]+w[i].size();
        rep(j,1,(int)sum[i].size()-1) sum[i][j]=sum[i][j-1]+w[i][j];
    }
    rep(i,0,n) rep(j,0,c[cnt]) dp[i][j]=1e19;
    dp[0][0]=0;
    rep(i,1,cnt) 
        rep(j,1,c[i]) 
            if (j*2>=c[i])
            {
                dp[i][j]=dp[i-1][j];
                rep(k,1,min(j,c[i]-c[i-1])) if (c[i]-c[i-1]-k<=j*2-c[i-1]-2*k)
                    chkmin(dp[i][j],dp[i-1][j-k]+sum[i][k-1]);
            }
                
    db ret=1e19;
    rep(i,1,n) chkmin(ret,dp[cnt][i]);
    return ret<=0;
}

int main()
{
    file();
    read(n);
    rep(i,1,n) read(a[i].a);
    rep(i,1,n) read(a[i].b);
    sort(a+1,a+n+1);
    db l=0,r=1e10;
    while (r-l>=1e-7)
    {
        db mid=(l+r)/2;
        if (judge(mid)) r=mid;
        else l=mid;
    }
    printf("%lld",(ll)ceil(l*1000));
    return 0;
}

T2

第一問非常無趣,\(a_i=a_i-i\)後求一個\(a\)的最長不下降子序列就好了。

\(dp_i\)表示以\(i\)結尾的最長不降子序列長度。

第二問考慮DP,設\(f_i\)表示\(i\)不變,前\(i\)個都已經變為不降序列的最小代價。

\(f_i\)可以由\(f_j\)轉移而來,其中\(j<i\;\;\&\&\;\;dp_j=dp_i-1\)

註意到一個性質:這段區間中的\(k\)都滿足\(a_j>a_k \; \; or \;\; a_k>a_i\)

所以可以感受一下,\(j\rightarrow i\)之間的\(a\)必然會變成這樣:一段都等於\(a_j\)

,後一段等於\(a_i\)。如果不是這樣也可以調整成這樣。

然後暴力枚舉分界點即可。

復雜度似乎很大,但數據隨機,也就這麽跑過去了。

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 50500 
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char sr[1<<21],z[20];int C=-1,Z=0;
    inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    inline void print(register int x)
    {
        if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++C]=z[Z],--Z);sr[++C]='\n';
    }
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n;
int a[sz];

int dp[sz];
int mn[sz],top;

ll f[sz];
vector<int>v[sz];

int main()
{
    file();
    read(n);
    rep(i,1,n) read(a[i]),a[i]-=i;
    mn[0]=-1e9;
    a[n+1]=1e9;
    rep(i,1,n+1)
    {
        int pos=upper_bound(mn,mn+top+1,a[i])-mn-1;
        dp[i]=pos+1;
        if (pos==top) ++top,mn[top]=1e9;
        chkmin(mn[pos+1],a[i]);
    }
    printf("%d\n",n-dp[n+1]+1);
    rep(i,1,n+1) v[dp[i]].push_back(i);
    v[0].push_back(0);
    rep(i,1,n+1) f[i]=1e18;
    a[0]=-1e9;f[0]=0;
    rep(i,1,n+1)
    {
        for (int x:v[dp[i]-1]) if (a[x]<=a[i]&&x<i)
        {
            ll sum1=0,sum2=0;
            rep(j,x+1,i-1) sum2+=abs(a[j]-a[i]);
            ll cur=sum2;
            rep(j,x+1,i-1) sum1+=abs(a[j]-a[x]),sum2-=abs(a[j]-a[i]),chkmin(cur,sum1+sum2);
            chkmin(f[i],f[x]+cur);
        }
    }
    cout<<f[n+1];
    return 0;
}

T3

這題沒做出來,我的腦子呢……

直接給鄰接矩陣做矩陣乘法,相當於是DP。

由於食人魚的周期特別短,取\(12\)為一周期,把每一個時刻的鄰接矩陣處理出來,然後瞎乘一通就好了。

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 55 
    #define mod 10000
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char sr[1<<21],z[20];int C=-1,Z=0;
    inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    inline void print(register int x)
    {
        if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++C]=z[Z],--Z);sr[++C]='\n';
    }
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,m;
ll K;
int F,T;

struct Matrix
{
    ll a[sz][sz];
    Matrix(){rep(i,1,n) rep(j,1,n) a[i][j]=0;}
    void init(){rep(i,1,n) a[i][i]=1;}
};
inline Matrix operator * (const Matrix &a,const Matrix &b)
{
    Matrix ret;
    rep(i,1,n) rep(k,1,n) rep(j,1,n) (ret.a[i][j]+=a.a[i][k]*b.a[k][j]%mod)%=mod;
    return ret;
}

Matrix a,s[13],S;
void ksm(ll y)
{
    Matrix ret;ret.init();
    for (;y;y>>=1,S=S*S) if (y&1) ret=ret*S;
    S=ret;
}

int p[5];

int main()
{
    file();
    read(n,m,F,T,K);++F,++T;
    int x,y;
    rep(i,1,m) read(x,y),++x,++y,a.a[x][y]++,a.a[y][x]++;
    rep(i,1,12) s[i]=a;
    read(m);
    while (m--)
    {
        read(x);
        rep(i,0,x-1) read(p[i]),++p[i];
        rep(i,1,12) 
        {
            y=p[i%x];
            rep(j,1,n) s[i].a[j][y]=0;
        }
    }
    S.init();
    rep(i,1,12) S=S*s[i];
    ksm(K/12);
    rep(i,1,K%12) S=S*s[i];
    cout<<S.a[F][T];
    return 0;
}

T4

事情開始變得惡心起來。

這題我會專門寫一篇博客的,等下放上來。


T5

暫時還不會,以後搞。

3.14雜題選講