校内集训20180920

$T1:$(Loj2333

铁路沿线$N$个车站,车分三种:快,慢,次快车。慢车走1个站花$A$单位时间,每站都停;快车走1个站花费$B$单位时间,只在$M$个站停,停靠站输入给出(起点$1$和终点$N$必停);次快车走1个站花费$C$单位时间,一共在$K$个站停,且必须在快车停靠站停,剩下$K-M$个停靠站随意建造。速度$A,B,C$满足$B<C<A$。求如何建造次快车停靠站使得在$T$分钟内能到达的车站尽量多。(所有移动均为单向,$1->N$方向)

(到达:停靠在此站。每个车站单独计数,即统计有几个车站从$1$走到该车站花费$T$分钟以内。)

$Nleq 10^9,Mleq Kleq 3000$。

题解:

第一遍看根本没看懂,$JOI$考的都是读题能力么……

看懂的话应该很快发现由于$B<C<A$,整个图被快车站分成了$M-1$个块。

修建的任意一个次快车站只能影响到它所在的块内在它后面的车站。

(块外的车站可以通过快车走到,并且不影响次快车在该块内的修建(所有快车停靠站都是次快车停靠站)为什么还要走次快车?)

显然,已知速度,时间,每个块内修建$k$个次快车站的贡献是可以计算的。

那么即可得到$O(M imes K^2)$的$dp$,$dp[i][j]$表示处理到第$i$个块,一共修建了$j$个次快车站时能到达的最多车站。

枚举该块内修建了$k$个转移即可。

好像过不了,考虑优化。

容易发现一个块内每放一个车站的贡献值是递减的。($V$不变,$T$减少,$S$便减少)

由此产生一个很重要的推论:一个块内按贡献值从大到小排序依次修建,一定是合法的。

(不会出现一个块内修建第$k$个的贡献比第$k-j$个贡献大的情况,否则你没修建到第$k-j$个怎么修建第$k$个?)

那么既然每个块互不影响,所有块均满足该推论。

于是可以将所有块内修建第$i$个块的贡献值扔到一个优先队列里,每次操作取出队首,在此修建,累加答案,并将其出队。操作$k$次得到答案。

复杂度$O(K imes log(M imes K))$。

这题想了$30min$,标程写了$1h-$,对拍写了$2h+$写挂了,最后直接交标程$A$了,我尼玛……

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;
#define MAXN 3005
#define MAXM 500005
#define INF 0x7fffffff
#define ll long long

priority_queue<ll> q;
ll S[MAXN],sgo[MAXN][MAXN];
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

int main(){
    ll N=read(),M=read(),K=read();K-=M;
    ll A=read(),B=read(),C=read(),T=read();
    for(ll i=1;i<=M;i++) S[i]=read();
    sort(S+1,S+1+M);
    for(ll i=1;i<=M;i++){
        if(i==1) continue;
        //cout<<i<<endl;
        ll slen=S[i-1]-1;ll stim=T-(slen*B);
        //cout<<stim<<endl;
        if(stim<0) break;
        //cout<<stim<<endl;
        sgo[i][0]=stim/A;
        ll sum=sgo[i][0];
        //cout<<sum<<":"<<S[i-1]<<":"<<S[i]<<endl;
        if(sum+S[i-1]>=S[i]){
            sgo[i][0]-=(sum+S[i-1]-S[i]+1);
            sgo[i][0]=max(sgo[i][0],(ll)0);
            //cout<<i<<":"<<sgo[i][0]<<" ";
            continue;
        }
        //cout<<i<<":"<<sgo[i][0]<<" ";
        for(ll j=1;j<=K;j++){
            if(stim<(sum+1)*C) break; 
            sgo[i][j]=(stim-(sum+1)*C)/A+1;
            sum+=sgo[i][j];
            if(sum+S[i-1]>=S[i]){
                sgo[i][j]-=(sum+S[i-1]-S[i]+1);
                sgo[i][j]=max(sgo[i][j],(ll)0);
                //cout<<sgo[i][j]<<" ";
                break;
            }
            //cout<<sgo[i][j]<<" ";
        }
        //cout<<endl;
    }
    ll ans=0;
    for(ll i=2;i<=M;i++){
        if((S[i]-1)*B>T) break;
        ans++;
    }
    //cout<<ans<<endl;
    for(ll i=1;i<=M;i++){
        //cout<<i<<":"<<sgo[i][0]<<endl;
        ans+=sgo[i][0];
        //cout<<ans<<endl;
    }
    //cout<<ans<<endl;
    for(ll i=1;i<=M;i++)
        for(ll j=1;j<=K;j++)
            q.push(sgo[i][j]);
    for(ll i=1;i<=K;i++){ans+=q.top();q.pop();}
    printf("%lld
",ans);
    //cout<<ans<<endl;
    /*
    for(ll i=1;i<=M;i++){
        if(S[i]==1) continue;
        ll slen=S[i-1]-1;ll stim=slen*B;
        ll st=T-stim;ll sgo[0]=st/A,cnt=0;
        if(st<0) break;
        for(ll k=0;k<=K;k++) dp[i][k]=dp[i-1][k]+sgo[0]+1;
        ll num=sgo[0];
        if(num>=S[i]-S[i-1]-1) continue;
        for(ll k=1;k<=K;k++){
            sgo[k]=(st-(sgo[i-1]*C))/A;
            num+=sgo[k],cnt++;
            if()
        }
        for(ll k=1;k<=K;k++){
            for(ll l=1;l<=k;l++){
                
                sgo=
                dp[i][k]=dp[i-1][k]+
            }
        }
    }*/
    return 0;
}
/*
12 3 4
10 1 2
30
1
11
12
*/

T2(Loj2334):

给你一个$N imes M$的矩阵,将其划分成两块使得两块中的极差($max-min$)最大者最小。划分不能出现“突出”或“凹陷”。

原句:对于每一行/列,如果我们将这一行/列单独取出,这一行/列里同省的任意两个区块互相连接。这一行/列内的所有区块可以全部属于一个块。

$N,Mleq 2000$。

题解:

看懂了非常好做,考点依旧是读题。

根据题意第一个国家只可能处在第二个的左上,右上,左下,右下。

那么每个方向二分答案求解即可。

但如果看了上一题的时间花费就知道我已经没时间写这题了……

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 3005
#define MAXM 500005
#define INF 0x7fffffff
#define ll long long

int N,M,maxn,minn;
int A[MAXN][MAXN];
int tmp[MAXN][MAXN];
inline int read(){
    int x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

bool check(int x){
    int pos=M;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=pos;j++)
            if(A[i][j]-minn>x)
                {pos=j-1;break;}
        for(int j=pos+1;j<=M;j++)    
            if(maxn-A[i][j]>x) 
                return 0;
    }
    return 1;
}

void sswap(){
    for(int i=1;i<=N;i++)
        for(int j=1;j<=M;j++)
            tmp[j][N-i+1]=A[i][j];
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++)
            A[i][j]=tmp[i][j];
    return;    
}

int main(){
    N=read(),M=read();
    maxn=-INF,minn=INF;
    for(int i=1;i<=N;i++)
        for(int j=1;j<=M;j++){
            A[i][j]=read();
            maxn=max(maxn,A[i][j]);
            minn=min(minn,A[i][j]);
        }
    int mans=INF;
    for(int i=1;i<=4;i++){
        int l=0,r=maxn-minn,ans=-INF;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        mans=min(ans,mans);
        sswap();swap(N,M);
    }
    printf("%d
",mans);
    return 0;
    
}

T3(Loj2325):

bzoj4832升级版,攻击次数从$50$变为$10^{18}$(炉石传说没有外挂),求期望对$998244353$取模的值。

题解:

弱化版题目没什么问题吧……不管用什么奇怪的顺序转移应该都是能$A$的。

多说一句,如果直接预处理$dp[i][j][k][l]$表示还剩$i$次攻击,剩余$1,2,3$血奴隶主的个数分别为$j,k,l$个,

每次从还剩$i-1$次攻击转移过来(也就是反向建拓扑图)的话,可以通过$T$极大的数据。

然后我们发现这个题的攻击次数是$10^{18}$。

(一回合打$50$火妖法还能够到(还把我超生德直接打死),但这尼玛$10^{18}$难不成要无限回合火球法?)

再枚举$N$进行转移显然不可取,所以进行矩阵优化。

容易发现总状态数为固定的$165$个。(模型:$0,1,2,3$每种$k$个和为$7$,$k$可以为$0$)

那么我们将所有状态排成一列,形成一个$1 imes 165$的初始矩阵。

状态间互相转移的路径是相同的,每次转移的概率也是相同的,

那么可以构造一个$165 imes 165$的转移矩阵,答案相当于${初始矩阵 imes (转移矩阵^N)}$。

使用矩阵快速幂即可。(需要卡常)

但$165 imes 165$不好维护答案,再加$1$维变为$166 imes 166$即可维护打脸期望。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 100005
#define MAXM 10
#define MAXK 170
#define MAXL 63
#define INF 0x7fffffff
#define ll long long

const ll MOD=998244353;
const ll mod=(0x7fffffffffffffffll/MOD-MOD)*MOD;
ll inv[MAXK],sta[MAXM][MAXM][MAXM],res[MAXK],tmp[MAXK],num;
struct Matrix{
    ll A[MAXK][MAXK];
    Matrix() {memset(A,0,sizeof(A));}
}m[MAXL+5];

inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

inline ll power(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans*=a%MOD,ans%=MOD;
        b>>=1,a*=a,a%=MOD;
    }
    //cout<<ans<<endl;
    return ans%MOD;
}

inline void mul(Matrix a){
    memset(tmp,0,sizeof(tmp));
    for(ll i=1;i<=num+1;i++){
        for(ll j=1;j<=num+1;j++){
            //if(a[j]*b.A[j][i]<0) cout<<a[j]<<" "<<b.A[j][i]<<endl;
            tmp[i]+=res[j]*a.A[j][i];
            if(tmp[i]>=mod) tmp[i]-=mod;
        }
        tmp[i]%=MOD;
    }
    memcpy(res,tmp,sizeof(tmp));
    return;
}

inline Matrix sq(Matrix a){
    Matrix ans;
    for(ll i=1;i<=num+1;i++)
        for(ll j=1;j<=num+1;j++){
            for(ll k=1;k<=num+1;k++){
                ans.A[i][j]+=a.A[i][k]*a.A[k][j];
                if(ans.A[i][j]>=mod) ans.A[i][j]-=mod;
            }
            ans.A[i][j]%=MOD;
        }
    return ans;
}
int main(){
    //freopen("1.txt","w",stdout);
    ll T=read(),M=read(),K=read();num=0;
    for(ll i=1;i<=K+1;i++) inv[i]=power(i,MOD-2);
    if(M==1) 
        for(ll i=0;i<=K;i++) 
            sta[i][0][0]=++num; 
    if(M==2) 
        for(ll i=0;i<=K;i++) 
            for(ll j=0;j<=K-i;j++) 
                sta[i][j][0]=++num;
    if(M==3)
        for(ll i=0;i<=K;i++)
            for(ll j=0;j<=K-i;j++)
                for(ll k=0;k<=K-i-j;k++)
                    sta[i][j][k]=++num;
    if(M==1){
        for(ll i=0;i<=K;i++){
            ll P=inv[i+1];
            if(i) m[0].A[sta[i][0][0]][sta[i-1][0][0]]=P*i%MOD;
            m[0].A[sta[i][0][0]][sta[i][0][0]]=m[0].A[sta[i][0][0]][num+1]=P;
        }
    }
    if(M==2){
        for(ll i=0;i<=K;i++)
            for(ll j=0;j<=K-i;j++){
                ll P=inv[i+j+1];
                if(i) m[0].A[sta[i][j][0]][sta[i-1][j][0]]=P*i%MOD;
                if(j) m[0].A[sta[i][j][0]][sta[i+1][(i+j<K)?j:j-1][0]]=P*j%MOD;
                m[0].A[sta[i][j][0]][sta[i][j][0]]=m[0].A[sta[i][j][0]][num+1]=P;
            }
    }
    if(M==3){
        for(ll i=0;i<=K;i++)
            for(ll j=0;j<=K-i;j++)
                for(ll k=0;k<=K-i-j;k++){
                    ll P=inv[i+j+k+1];
                    if(i) m[0].A[sta[i][j][k]][sta[i-1][j][k]]=P*i%MOD;
                    if(j) m[0].A[sta[i][j][k]][sta[i+1][j-1][(i+j+k<K)?k+1:k]]=P*j%MOD;
                    if(k) m[0].A[sta[i][j][k]][sta[i][j+1][(i+j+k<K)?k:k-1]]=P*k%MOD;
                    m[0].A[sta[i][j][k]][sta[i][j][k]]=m[0].A[sta[i][j][k]][num+1]=P;
                }
    }
    m[0].A[num+1][num+1]=1;
    for(ll i=1;i<=MAXL;i++) m[i]=sq(m[i-1]);
    while(T--){
        ll N=read(),cnt=0;
        memset(res,0,sizeof(res));
        if(M==1) res[sta[1][0][0]]=1;
        if(M==2) res[sta[0][1][0]]=1;
        if(M==3) res[sta[0][0][1]]=1;
        while(N){
            if(N&1) mul(m[cnt]);
            N>>=1,cnt++;
        }
        printf("%lld
",res[num+1]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/YSFAC/p/9689899.html