test20190803合宿NOIP 19

60 + 100 + 0 = 160

グリード大陸

クレイジーアリの攻撃の顔は、小さなFFのタワーの防衛は、人間がグリードアイランド上だけベイにアリです......失敗しました。さて、後方FFは海で、フロントスーパーアリのバリエーションです。小型FF有望な未来がありますが、彼はここでの生活の損失にしたくなかったので、彼は最後のバッチ変換SCVは、アリの攻撃をブロックするように地雷を配置された彼の人を送りました。

防衛の小さなFF最後の行は、長さNトレンチで既に[L、R]で地雷の敷設前に異なるいる同じにすることができる多数の小さなFF様々な鉱山、SCV及び各セグメントの敷設を有しています。ケースが緊急となっているので、いくつかの点で小さなFFあなたは[L「R」]の範囲で持っているどのように多くの異なる地雷求めることができる、彼はあなたができるだけ早く返信したいと考えています。

入力フォーマット:
整数の最初の二つの行為は、mおよびnは、nは長さの行を表し、mは総回数と小鉱山SCVのFF問い合わせを示しています。
次に、m行は、三整数Q、L、Rの各行は、ある; Q = 2の場合= 1、Qは、布のこの間隔鉱山一種類の場合SCV [L、R]ことを示し、小さな現在のクエリFFは、[前記しましたL、R]は鉱山の多くの種類が存在する範囲です。

出力フォーマット:
小型FFごとに問い合わせ、回答出力(単線)、地雷の総数の現在の範囲ことを示しています。

サンプル入力:
5 4
1. 1. 3。
2 2 5
1 4 2。
2. 3. 5

サンプル出力:
1
2

データ範囲:
データの30%:0 <= N、M < = 1000;
100%のデータ:0 <= N、M < = 10 ^ 5。

制限時間:
LSの
スペースの制約:
50M

問題の解決策

+は、セグメントの範囲の数が含まれている区間内に含まれるセグメントの数 - エンドポイントの答え=間隔内で発見された問題の数を見てください。

最初のブロック、ソートアウトアウト右端ブロックの左端のような\(O(n個の\ SQRT { N} \ログN)\) 放棄されたアプローチ。

そして、バランスの取れたツリーが直接ツリーラインは、やる減算をサポートしていないコードセット・イテレータを見つけることができます設定を見つけます。

だから私は、学校の__gnu_pbdsに行ってきました::木、長い時間に従事し、最終的に右。時間複雑\(O(N \ ^ 2をログ)\)、60分。トイレのブロックアルゴリズム、80と言う人に聞く......

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#define pbds __gnu_pbds
#define co const
#define il inline
template<class T> T read(){
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;
}
template<class T>il T read(T&x){
    return x=read<T>();
}
typedef pbds::tree<std::pair<int,int>,pbds::null_type,std::less<std::pair<int,int> >,pbds::rb_tree_tag,pbds::tree_order_statistics_node_update> tree;

co int N=100000+1,INF=1e9;
#define lc (x<<1)
#define rc (x<<1|1)
int n,m;
tree ls,rs,s[N<<2];
void insert(int x,int l,int r,int p,int v,int id){
    s[x].insert(std::make_pair(v,id));
    if(l==r) return;
    int mid=(l+r)>>1;
    if(p<=mid) insert(lc,l,mid,p,v,id);
    else insert(rc,mid+1,r,p,v,id);
}
int qin(int x,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr) return s[x].order_of_key(std::make_pair(qr,INF))-s[x].order_of_key(std::make_pair(ql,-INF));
    int mid=(l+r)>>1;
    if(qr<=mid) return qin(lc,l,mid,ql,qr);
    if(ql>mid) return qin(rc,mid+1,r,ql,qr);
    return qin(lc,l,mid,ql,qr)+qin(rc,mid+1,r,ql,qr);
}
int qout(int x,int l,int r,int ql,int qr){
    if(r<ql) return s[x].order_of_key(std::make_pair(INF,INF))-s[x].order_of_key(std::make_pair(qr,INF));
    int mid=(l+r)>>1;
    if(ql<=mid+1) return qout(lc,l,mid,ql,qr);
    return qout(lc,l,mid,ql,qr)+qout(rc,mid+1,r,ql,qr);
}
int main(){
//  freopen("testdata.in","r",stdin),freopen("testdata.ans","w",stdout);
    read(n),read(m);
    for(int i=1;i<=m;++i){
        int q=read<int>(),l=read<int>(),r=read<int>();
        if(q==1){
            ls.insert(std::make_pair(l,i)),rs.insert(std::make_pair(r,i));
            insert(1,1,n,l,r,i);
        }
        else{
            int ans=0;
            ans+=ls.order_of_key(std::make_pair(r,INF))-ls.order_of_key(std::make_pair(l,-INF));
            ans+=rs.order_of_key(std::make_pair(r,INF))-rs.order_of_key(std::make_pair(l,-INF));
            ans-=qin(1,1,n,l,r);
            ans+=qout(1,1,n,l,r);
            printf("%d\n",ans);
        }
    }
    return 0;
}

正解はフェンウィックツリーで、我々は我々が唯一の始まりをマークし、電流範囲の終わりに、私たちがチェックする必要があり、各埋め地雷を必要とする、我々は唯一の電流範囲の最後に1をチェックする必要が先頭に番号が含まれています(A地雷の始まりを表します)と現在位置までの間隔1を超える埋葬された私たちは、いくつかの鉱山の前で見オーバー埋めたち地雷、の代わりに末尾の数字(末尾が含まれている前に、我々がチェック!)そして、この操作を行います貧しい人々は、我々は現在の間隔で存在しているどのように多くの鉱山を知ることができます。時間複雑\(O(N \ログN )\)

マルチプレイバックパック

DDや友人はそれを登るしよう!彼らはK個体の総あり、誰もがパッケージを運ぶでしょう。これらのパッケージの容量は、同じであるV.です アイテムのN種類の合計のバックパックに入れることができ、各項目は、所与の容積および価値を有します。
DDでは、次のようにバックパックプログラムのための合理的な取り決めがあるようです。

  1. それぞれのバックパックは、物品のパックの容量の合計容量に正確に等しいでいっぱい。
  2. ほとんどの1つ、2つの異なるパッケージが各袋の各項目の同じ記事中に存在することができます。
  3. 任意の2人は、項目のリストは、彼らはまったく同じ袋にすることはできません。

上記の要件を満たすの前提の下で、すべての袋のすべての商品の合計値が最大でどのくらいですか?

入力形式:
K、V、N.:最初の行は三つの整数が含まれています
各行は2つの整数を有し、この記事の体積を表す値、第N行を開始します。

出力形式:
出力のみ整数、すなわち、上記の要件を満たす前提のすべての項目の合計値の最大値。

サンプル入力:
2 10 5
3 12は、
7〜20である。
2. 4
5 6。
1. 1。

サンプル出力:
57

データ範囲:
K <= 50の合計数。
各ナップザック容量V <= 5000。
商品N <= 200の種類の数。
他の正の整数ではありません5,000以上。
入力されたデータは、それがプログラムの要件を満たしていることを確認します。

制限時間:
1S
スペースの制限:
128M

問題の解決策

私は問題のバックパックを拡張しないだろうことがわかった......これはk最適解のバックパックです。

F(i、j)はボリュームI、J最適解を表します。そして、両手スキャンに移しました。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read() {
    T x=0,w=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar())if(c=='-') w=-w;
    for(; isdigit(c); c=getchar()) x=x*10+c-'0';
    return x*w;
}
template<class T>il T read(T&x) {
    return x=read<T>();
}
using namespace std;
int k,v,n,ans,cnt,now[55];
int V[288],W[288],f[5008][55];
int main() {
    read(k),read(v),read(n);
    for(int i=0; i<=5000; i++)
        for(int j=0; j<=50; j++)f[i][j]=-20021003;
    f[0][1]=0;
    for(int i=1; i<=n; i++)
        read(V[i]),read(W[i]);
    for(int i=1; i<=n; i++)
        for(int j=v; j>=V[i]; j--) {
            int c1=1,c2=1,cnt=0;
            while(cnt<=k) {
                if(f[j][c1]>f[j-V[i]][c2]+W[i])
                    now[++cnt]=f[j][c1++];
                else now[++cnt]=f[j-V[i]][c2++]+W[i];
            }
            for(int c=1; c<=k; c++) f[j][c]=now[c];
        }
    for(int i=1; i<=k; i++) ans+=f[v][i];
    printf("%d",ans);
}

新しい魔法の薬

DD MMの魔術師は、誕生日プレゼントを送りたいが、彼は十分な金を持っていませんでした。魔法熟練DDは、当然いくつかの金貨を稼ぐ策定ポーションに巧妙な使用のスキルを考えます。
N型のDDの合計がポーション(数1..N)を知っている、また、(特許1..Mに)M法をマスターポーションを策定。次のように各ポーションの準備彼の処分の方法を単純に表現されています。いくつかのポーションは各ボトルを攪拌特定の魔法をかけながらるつぼ、杖に注ぎ、その後、適切下で濃縮し、あなたが得ることができますポーションの新しいボトル。
様々なポーションの取得の価格よりも若干低い価格ではありのみポーションのすべての種類を販売していない森、魔法店ですが、また。DDの希望的観測はこれです:彼女は原料として、いくつかの魔法の薬を買いに店にコインを保存し、自分のVを持つすべての最初の、そして自宅で一日の準備にしようと、魔法店に販売し、最終的な完成品との準備。
しかし、魔法の修理のために、DDは一日だけのK魔法一度に適用することができます。
DDは、彼は一日の時間でそれを最大限に活用することができますどのくらいの金、あなたは彼が数学を行う手助けしたいですか?

入力フォーマットは:
N、M、V、K:最初の行は4つの整数を有しています 。
第N行を開始し、二つの整数、iは二つの整数の1を+行を有し、各行は、購入価格と、i番目のポーションの販売価格を示します。
N + 2行のM列は、整数の複数を有し、各行は、一のポーションDD公知の製剤方法を示し、開始します。調製の第一ポーション整数そのような方法は、ポーションボトル数を仕上げることができる:各行は、このされるフォーマット 次の整数N(N <N)、この調製方法は、nは、ボトルの材料を表す必要とします。ここでnは整数、nは原料ボトルの数です。

出力フォーマット:
ジャスト出力は金貨の最大数を表す整数を獲得することができます。

サンプル入力:
4 2 6 3
。1 0
1 0
。5 3
20 15である
3 2 1 2
4 3 1 2 3。

サンプル出力:
12

データ範囲:
シロップタイプN <= 60。
製剤番号M <= 240の方法。
コインの初期数V <= 1000。
日あたりのマジックナンバーK <= 30に投与することができます。

制限時間:
1S
スペースの制限:
128M

問題の解決策

私は状態が、書き込みに時間がないと思います。F(i、j)が魔法のほとんどのj回で最小のコストで、私のすべての項目を表します。それから私は、転送は、ほとんどの短絡を思い付いたのか分かりません。

まず金の魔法を使用する数、および魔法の作品は、その仕事を移しました。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
int n,m,V,cnt;
int tmp[61][31];//表示当前处理到i次用了j魔法的最低费用
int f[61][31];//表示第i件物品用j次魔法组成的最低费用
//用tmp更新f
int dp[1001][31];//表示当前花费i元用了j次魔法的最大收益
//用dp[i][j]-i更新答案 
int v[61];//物品的售价 
int id[250],sum[250];//每种魔法的成品和需要的原材料个数
vector<int>s[250];//每种魔法所需的原料是什么 
int main()
{
    memset(f,63,sizeof(f)); 
    scanf("%d%d%d%d",&n,&m,&V,&cnt);
    for(int i=1;i<=n;i++) scanf("%d%d",&f[i][0],&v[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&id[i],&sum[i]);
        for(int j=1;j<=sum[i];j++)
        {
            int x; scanf("%d",&x);
            s[i].push_back(x);
        }
    }
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=m;j++)
        {
            memset(tmp,63,sizeof(tmp));
            tmp[0][0]=0;
            int T=i-1;
            for(int k=0;k<=T;k++)
                for(int l=1;l<=sum[j];l++)
                {
                    int x=s[j][l-1];
                    for(int a=0;a<=k;a++)
                        tmp[l][k]=min(tmp[l][k],tmp[l-1][k-a]+f[x][a]);
                }
            f[id[j]][i]=min(f[id[j]][i],tmp[sum[j]][T]);
            for(int k=i;k<=cnt;k++) f[id[j]][k]=min(f[id[j]][k],f[id[j]][i]);
        } 
    for(int i=1;i<=V;i++)
        for(int j=0;j<=cnt;j++)
        {
            for(int k=1;k<=n;k++)
                for(int l=0;l<=j;l++)
                    if(f[k][l]<=i)
                        dp[i][j]=max(dp[i][j],dp[i-f[k][l]][j-l]+v[k]-f[k][l]);
        }
    printf("%d",dp[V][cnt]);
    return 0;
}

おすすめ

転載: www.cnblogs.com/autoint/p/test20190803.html