HGOI 20191103am 题解

Problem A number

    使用一个$2^k$数集中每个元素的和表示数$n$,不同集合的数目有多少?

    对于$100\%$的数据满足$1 \leq n \leq 10^6$

  Solution : 

  $f[i][j]$表示使用不大于$2^i$的数组成集合,构成数$j$的不同集合数目。

  利用完全背包的思想,一开始$f[i+1][j] = f[i][j] + \sum_{k} f[i][j-k * 2^{i+1}]$

  显然可以通过滚动数组来优化,利用完全背包的思想,我们得到如下算法:

  一开始令$f[i+1][j] = f[i][j]$,然后从小到大考虑每一个$j$,用$f[i+1][j] += f[i+1][j-2^{i+1}]$的dp方程转移。

  时间复杂度$O(n log_2 n)$

# include<bits/stdc++.h>
# define int long long
# define MOD(x) ((x>=mo)?(x-mo):(x)) 
using namespace std;
const int N=1e6+10;
const int mo=1000000007;
int n,f[N];
signed main() {
    scanf("%lld",&n); f[0]=1;
    for (int i=0;(1<<i)<=n;i++) 
        for (int j=0;j<=n;j++)
            if (j>=(1<<i)) f[j]=MOD(f[j]+f[j-(1<<i)]);
    printf("%lld\n",f[n]%mo);
    return 0;
}
number.cpp

Problem B game

  有$n$个变量$x_1,x_2,...,x_n$,并给出$d$, 现在有$m$个限制$(a,b)$,满足$|x_a - x_b| \leq d$ 

  求出$max\{x_1,x_2,...,x_n\} - min\{x_1,x_2,...,x_n\}$的最大值,且满足上述限制。

  对于$100\%$的数据满足$n\leq 500$

  Solution : 

    首先答案一定是$d$的倍数,所以我们只需要将每一条边权变为$1$,来考虑这个问题。

    将每一条限制建图,我们不妨考虑从点$i$开始到点$i$结束的一个环。

    显然,要让这个环上的点都满足条件,其权值最大的点必然是这个环的中点。

    这个中点的权值,等价于从$i$为源点,到所有目标点$j$最短路的最大值。

    考虑不在这个环上的所有边,必然可以构造出一种合法状态使整个图满足条件。

    所以,本题的答案和求出这个图中最短路的最大值等价。

    直接用$floyd$即可,时间复杂度为$O(n^3)$

#include<bits/stdc++.h>
using namespace std;
int n;
int f[55][55];
int d;
int solve(){
    cin >> n; cin >> d;
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            char c;
            cin >> c;
            if (c == 'N') f[i][j] = 0x3f3f3f3f;
            else f[i][j] = 1;
            if (i == j) f[i][j] = 0;
        }
    }
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
    
    int ans = 0;
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++)
            ans = max(ans, f[i][j]);
    }
    if (ans > 100000) cout << -1 << endl;
    else cout << (long long)ans * d << endl;
}

int main(){
//  freopen("bridge.in","r",stdin);
//  freopen("bridge.out","w",stdout);
    int T;
    cin >> T;
    while (T--){
        solve();
    }
}
game.cpp

Problem C queue

  有一个$[1,n]$的排列,每一次可以将一个数移到排列的头部或者尾部。

   求出最小操作次数使得排列有序。

   对于$100\%$的数据满足$1 \leq n\leq 10^5$

  Solution : 

    本题有一个加强版$CF1223D$. (可以把那个代码粘贴过来,然后就A掉了)

    还是重新考虑这个弱化版本,甚至还不需要用离散化。

    一个答案的构造一定满足$l,l-1,l-2,...,1$依次放到队首,$r,r+1,...,n$依次放到队尾。

    于是我们可以$dp$,设$dp[i]$表示$i$之前的最长的长度,使得$i - dp[i]+1, i-dp[i] , ... i$这些数有序。

    那么最后的答案必然是$n - max(dp[i])$

    对于本题,等价于求出n - 连续最长上升子序列,时间复杂度为$O(n)$

# include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,a[N],dp[N],MinID[N],MaxID[N];
vector<int>tmp;
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
int main()
{
    int T=1; 
    while (T--) {
        tmp.clear();
        n=read();
        for (int i=1;i<=n;i++) {
            a[i]=read();
            tmp.push_back(a[i]);
        }
        sort(tmp.begin(),tmp.end());
        tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
        for (int i=1;i<=n;i++) {
            a[i] = lower_bound(tmp.begin(),tmp.end(),a[i]) - tmp.begin() + 1;
        }
        int sz = tmp.size();
        for (int i=1;i<=sz;i++) MinID[i]=0x3f3f3f3f,MaxID[i]=-0x3f3f3f3f; 
        for (int i=1;i<=n;i++) {
            MinID[a[i]] = min(MinID[a[i]],i);
            MaxID[a[i]] = max(MaxID[a[i]],i);
        }   
        dp[1]=1; int ans = sz-1;
        for (int i=2;i<=sz;i++) {
            if (MaxID[i-1] < MinID[i]) dp[i]=dp[i-1]+1;
            else dp[i]=1;
            ans = min(ans,sz-dp[i]);
        }
        printf("%d\n",ans); 
    } 
    return 0;
}
CF1223D
# include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,p[N],a[N],f[N],ans;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),p[a[i]]=i;
    for (int i=1;i<=n;i++) {
        f[i]=1;
        if (p[a[i]-1]<i) f[i] = max(f[i],f[p[a[i]-1]]+1);
        ans = max(ans,f[i]); 
    }
    printf("%d\n",n-ans);
    return 0;
}
queue.cpp

 

posted @ 2019-11-03 11:16  ljc20020730  阅读(127)  评论(0编辑  收藏  举报