"CSP-Sシミュレーションゲーム" 最初の2019


試験は非常に奇妙な感じ。
\(T1、T2 \)は、小さな特殊文の短いです。
\(T3 \)暴力的暴力よりも優先順位と呼ばれますまたは暴力しかし、私はなぜ知らない\(WA \)の摩耗を。
検査はまた、鉱山を再生すると...
実際には、料理は...原罪ああです
___

石を取るために少し奇妙T1

タイトル

こちらをクリック

診察室のアイデア

ほとんど自分のピットからは、開口部の試験\(5分\)恐ろしい爆発のうちコードにはゼロにすることができます\(01 \)をバックパックを。
幸いにも、あなたが自分自身の小さなデータカードに自分自身を配る前に、結果は数分で、その後、コードA \(80pts \)コード。
この方法は、第1のデータを分離します。
以下のために(A、B \)\ 2つのデータセット、それは暴力が終わってもよいことは明らかであり、これは問題ありません。
(C \)\データのセットの定義\(DP [I] \)石の数を有する:\(I \)ミン選択石がヒープ。
コード形のターンを参照してください。

#include<cstdio>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define cg (c=getchar())
inline int qread(){
    int x=0,f=1;char c;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return x*f;
}
template<class T>inline void qread(T& x){
    x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=200;
const int MAXK=2500;
const int INF=0x3f3f3f3f;

int n,m,k,a[MAXN+5],maxx;
int dp[MAXK+5];

inline void init(){
    qread(n,m,k);
    for(int i=1;i<=n;++i)qread(a[i]);
}

void dfs(const int i,const int x,const int w){
    if(w>k||x>m)return;
    maxx=Max(maxx,w);
    if(i>n)return;
    dfs(i+1,x+1,w+a[i]);
    dfs(i+1,x,w);
}

inline void getDp(){
    rep(i,1,k)dp[i]=INF;
    rep(i,1,n)rep(j,a[i],k)dp[j]=Min(dp[j],dp[j-a[i]]+1);
    dep(i,k,1)if(dp[i]<=m){maxx=i;break;}
}

signed main(){
    // freopen("stone.in","r",stdin);
    // freopen("stone.out","w",stdout);
    init();
    if(n<=20)dfs(1,0,0);
    else getDp();
    printf("%d\n",maxx);
    return 0;
}

正解

実際に節約するために、コード変更の私の試験の詳細、\(I \)状態の、\(J \)がなければならない列挙を降順
私は診察室の心を知らないwhat、と詳細が間違っています...

#include<cstdio>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define cg (c=getchar())
inline int qread(){
    int x=0,f=1;char c;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return x*f;
}
template<class T>inline void qread(T& x){
    x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=200;
const int MAXK=2500;
const int INF=0x3f3f3f3f;

int n,m,k,a[MAXN+5],maxx;
int dp[MAXK+5];

inline void init(){
    qread(n,m,k);
    for(int i=1;i<=n;++i)qread(a[i]);
}

void dfs(const int i,const int x,const int w){
    if(w>k||x>m)return;
    maxx=Max(maxx,w);
    if(i>n)return;
    dfs(i+1,x+1,w+a[i]);
    dfs(i+1,x,w);
}

inline void getDp(){
    rep(i,1,k)dp[i]=INF;
    rep(i,1,n)dep(j,k,a[i])dp[j]=Min(dp[j],dp[j-a[i]]+1);
    dep(i,k,1)if(dp[i]<=m){maxx=i;break;}
}

signed main(){
    // freopen("stone.in","r",stdin);
    // freopen("stone.out","w",stdout);
    init();
    if(n<=20)dfs(1,0,0);
    else getDp();
    printf("%d\n",maxx);
    return 0;
}

T2 "CCO 2017" のプロフェッショナルネットワーク

タイトル

こちらをクリック

診察室のアイデア

いわゆる情報コンテスト、実際には、データ指向プログラミング

データ範囲を見て、あなたはこれら2つのデータセットに対して分割カンニングすることができ、二つの点の前に良い嘘を見つけました...

#include<cstdio>
#include<algorithm>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
using namespace std;
#define cg (c=getchar())
inline int qread(){
    int x=0,f=1;char c;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return x*f;
}
template<class T>inline void qread(T& x){
    x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=2e5;
const int INF=0x3f3f3f3f;

int N,maxb,ans=INF;
struct node{
    int a,b;
    inline void input(){qread(a,b);if(a==0)b=0;}
    node(){}
    node(const int A,const int B):a(A),b(B){}
    bool operator<(const node t){return b==t.b?a<t.a:b<t.b;}
}p[MAXN+5];

inline void init(){
    qread(N);
    rep(i,1,N)p[i].input(),maxb=Max(maxb,p[i].b);
}

inline void dfs30(const int,const int,const int);
inline bool cmp30(const node _x,const node _y){
    return _x.a==_y.a?_x.b<_y.b:_x.a<_y.a;
}
inline void pts30(){
    sort(p+1,p+N+1,cmp30);
    dfs30(0,0,0);
}

inline void dfs30(int tot,int state,const int cost){
    if(tot==N)return (void)(ans=Min(ans,cost));
    if(cost>ans)return;

    rep(i,1,N)if(!(state&(1<<(i-1)))&&tot>=p[i].a)++tot,state|=(1<<(i-1));

    if(tot==N)return (void)(ans=Min(ans,cost));

    rep(i,1,N)if(!(state&(1<<(i-1))))
        dfs30(tot+1,state|(1<<(i-1)),cost+p[i].b);
}

inline bool cmp15(const node _x,const node _y){
    return _x.a>_y.a;
}
inline void pts15(){
    sort(p+1,p+N+1,cmp15);
    int l=1,r=N,tot=0;ans=0;
    while(l<=r){
        while(p[r].a<=tot)--r,++tot;
        if(r<l)break;
        if(p[l].a>tot)++ans;
        ++l,++tot;
    }
}

signed main(){
    // freopen("network.in","r",stdin);
    // freopen("network.out","w",stdout);
    init();
    if(maxb==1)pts15();
    else pts30();
    printf("%d\n",ans);
    return 0;
}

問題の解決策

実際には、この質問「オリンピックによる情報は」交通ルールを遵守されていない同じ目的を持って。- \ (\ テキストJZM} {\)

まず第一に、我々は状態のみんなとの友好関係を確立している確かにユニークです。
我々が続く場合は、\(私は\)友人の前に、我々は、支払った\(J \)の友人を。
まあ、確かに存在しない(kは(K \ NEQ I \ )\) 私たちにできます\(K \)すでに支払わ友達を作る前に\(jは\)友人を。
したがって、我々\(Jは\)取ることができる([0、N-1 \ \]) 番号のうち。
さて、私たちはこれだけ支払う必要があります\(N \)彼らはそれに対応したときに友人を\([0、N-1 ] \) の\(N \)に行くために数。
もし(I \)\定数で、その対応する値に費やされていない([a_iを、N-1 \ ] \) の数。
貪欲、我々は最初のものを満たさなければならない(B_i \)\我々はできるだけ過ごすますので、多数。
できるだけ番号に対応する間隔を作ることができるようにするために、我々はから検索する必要があります\(a_iを\)列挙を開始します。
以下のための\(I \)場合\([a_iを、N-1 ] \) の数に対応していなかった、それはのために課金されなければなりません。
だから、コードのこのセクションには、それをよく書くために

for(int i=1,loc;i<=N;++i){
    bool f=true;
    for(int j=p[i].a;j<N;++j)if(!vis[j]){
        vis[j]=true,f=false;
        break;
    }
    if(!f)ans+=p[i].b;
}

しかし、これは\(O(N ^ 2) \) アルゴリズム、それだけで最高に取得することができます(60pts \)\我々は互いに素セットの最適化を使用する必要があるので、。
までの時間の複雑さの近い\(O(N)は\) 私はその成長を遅らせるために、カウンターアッカーマン関数を救いました。

#include<cstdio>
#include<algorithm>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
using namespace std;
#define cg (c=getchar())
inline int qread(){
    int x=0,f=1;char c;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return x*f;
}
template<class T>inline void qread(T& x){
    x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=2e5;

int N,ans,pre[MAXN+5];bool vis[MAXN+5];
struct node{
    int a,b;
    inline void input(){qread(a,b);}
    node(){}
    node(const int A,const int B):a(A),b(B){}
    bool operator<(const node t){return b==t.b?a<t.a:b>t.b;}
}p[MAXN+5];

inline void init(){
    qread(N);
    for(int i=1;i<=N;++i)p[i].input();
    sort(p+1,p+N+1);
}

inline int findSet(const int u){
    return u==pre[u]?u:pre[u]=findSet(pre[u]);
}

inline void calc(){
    for(int i=1;i<=N;++i)pre[i]=i;
    for(int i=1,loc;i<=N;++i){
        loc=findSet(p[i].a);
        if(loc>=N)ans+=p[i].b;
        else pre[loc]=loc+1;
    }
}

signed main(){
    init();
    calc();
    printf("%d\n",ans);
    return 0;
}

T3「ZJOI2017」セグメントツリー

タイトル

こちらをクリック

診察室のアイデア

試験は、私だけの時間、複雑さを考える\(O(nm)を\)暴力アルゴリズムまだ暴力が、時間が不足していたが、
しかし、私はその正しさについて慎重に検討する必要はありません。
一般的な考え方は、このです:
呼掛けゾーンで\([L、R] \ ) 一定期間の間隔がある場合と、\([L 'R' ] \) と\(U \)サブツリーを持つツリーありません次に、演算部、上の\([L 'R' ] \) 貢献。
間隔ただし、ことは非常に問題がある\([L 'R'は '] \) に含まれている\([L、R&LT] \) および\(R' \)\(R 'は、' \)と同じではありませんサブツリー内、そして私の答えは、アルゴリズムになるだろう\(ANS \)が大きくなります。
間隔綴ることができますのために私が探していることになるそうするので\([L、R] \ ) 当然、ポイント増加し、\(ANS \)大きくなります。
付属のコード

#include<cstdio>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define cg (c=getchar())
inline int qread(){
    int x=0,f=1;char c;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return x*f;
}
template<class T>inline void qread(T& x){
    x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)if(c=='-')f=-1;
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=2e5;

struct node{
    int l,r,mid,d,lc,rc;
    node(){lc=rc=0;}
    node(const int L,const int R,const int M,const int D):l(L),r(R),mid(M),d(D){lc=rc=0;}
}tre[(MAXN<<1)+5];

int N,q,ncnt,ans;
int s[(MAXN<<1)+5],tail;

inline int buildtre(const int pre,const int l,const int r,const int d){
    int now=++ncnt;
    if(l==r)return tre[now]=node(l,r,0,d),now;
    tre[now]=node(l,r,qread(),d);
    tre[now].lc=buildtre(now,l,tre[now].mid,d+1);
    tre[now].rc=buildtre(now,tre[now].mid+1,r,d+1);
    return now;
}

inline void init(){
    qread(N);
    buildtre(0,1,N,1);
}

void getNode(const int i,const int l,const int r){
    if(l<=tre[i].l&&tre[i].r<=r)return (void)(s[++tail]=i);
    if(l<=tre[i].mid)getNode(tre[i].lc,l,r);
    if(r>tre[i].mid)getNode(tre[i].rc,l,r);
}

inline void calc(const int lca,const int u){
    while(tail>0)ans+=tre[s[tail--]].d+tre[u].d-2*tre[lca].d;
}

void algor(const int i,const int u,const int l,const int r){

    // printf("algor : %d %d %d %d\n",i,u,l,r);

    if(u==i){
        getNode(i,l,r);
        calc(i,i);
        return;
    }

    if(tre[i].lc<=u&&u<tre[i].rc&&r<=tre[i].mid)algor(tre[i].lc,u,l,r);
    else if(u>=tre[i].rc&&l>tre[i].mid)algor(tre[i].rc,u,l,r);
    //以上皆为同方向的子树时
    else{
        //在不同向
        //此时一定可以保证, i 即为他们的 lca
        if(u>=tre[i].rc&&l<=tre[i].mid){//当询问区间的 部分/全部 在左边, 点在右边时
            getNode(tre[i].lc,l,Min(tre[i].mid,r));
            calc(i,u);
            if(r>tre[i].mid)algor(tre[i].rc,u,tre[i].mid+1,r);
        }
        else if(tre[i].lc<=u&&u<tre[i].rc&&r>tre[i].mid){//当询问区间的 部分/全部 在右边, 点在左边时
            getNode(tre[i].rc,Max(tre[i].mid+1,l),r);
            calc(i,u);
            if(l<=tre[i].mid)algor(tre[i].lc,u,l,tre[i].mid);
        }
        /*
        else if(u==i){//当点就是当前的点的时候, 直接在其子树中寻找区间的点进行计算
            getNode(i,l,r);
            calc(i,i);
        }
        */
    }
}

inline void getQuery(){
    int q=qread(),u,l,r;
    while(q--){ans=0;
        qread(u,l,r);
        algor(1,u,l,r);
        printf("%d\n",ans);
    }
}

signed main(){
    // freopen("0.in","r",stdin);
    // freopen("tree.out","w",stdout);
    init();
    // puts("finished input!");
    getQuery();
    return 0;
}

正解

より困難である符号化データ構造。
まず、我々は間隔の問題を解決するために尋ねました。
以下、この絵を見て
ここに画像を挿入説明
絵があるの内側実は、このトピックが記載されています。
我々はインターバルにアクセスする必要があると([2,4] \)\、どのようにそれを行うには?
方法一
これは見つけていないようですが、あなたは...肉眼で見ることができ、\(OJ \)アップ...
しかし、我々は我々が探していることはノードであることがわかります\((2-3)\)\((4-4) \) 。
方法二
通常のツリーライン区間のように使用することができますツリーはおそらく、このような幅広いセグメントのため、このような複雑さを見つけることだった\(O(n)と\)です。
方法三
それに類似してもよい(\ \テキスト{zkw} \ ) セグメントツリーモード。
私たちは、間隔をアクセスする([2,4] \)\から左、その後、(\(1-1))\右から開始するように、\((5-5)\)一緒に登り始めました。
左ポイント上昇、父場合、息子はそれが左ノードであることが判明し、その親の権利の息子が確実であることは、我々はポイントを探しているということです。
右ポイント上昇した場合、彼は親ノードの右の息子だったことが判明し、その親ノードの左の息子は、私たちが探していることのポイントです。
それはおそらく、検索の結果であるここに画像を挿入説明
中、私たちが注意を払うためのポイントは、特別なまで赤ペンで囲んだ青い我々はポイントを取得しなければならないことを強調しながら、。
そうすることの時間複雑そう?言うまでもなく、\(O(N)\)

これらの方法は脇に置くされ、私たちが要求するものを見てみましょう。
タイトルを与えると思われる\ [ANS = \ sum_ {V \ Sで[L、R]} DIS(U、V)\] 注文\(D [U] \)ポイント:\(U \)の深さ。
私たちはすることができます(DIS(U、V)\ \) に置き換え(LCA \)\(時間複雑\(O(LOGN)\)の周りに、無視しない)があり、その後、表現するために[ANS = \ \ sum_ {V \ Sで[L 、R]} D [U]は+ D [V] -2 \ CDOTのD [LCA(U、V)] \] 我々は最後に処理できると仮定し、我々は満足見つける(\ Vを\ S [L、R]で \) ポイント合計)T \(\、次いで式がさらに拡大することができる:\ [ANS = T \ CDOT D [U] + \ sum_ {SにおけるV \ [L、R&LT ]} D [V] - \ sum_ {SにおけるV \ [L、R]} 2 \ CDOT D [LCA(U、V)] \] しかし、もはやこの式が現れるが、簡略化の下です。

でも、私たちを見て、バックステッピング、私たちは外に二つの方法を分析しました無視最初の男
私たちが使用している場合方法二、我々は何の最適化、唯一の標準的な慣行を持つツリーライン、時間の複雑さやを行うにはいないようだ\(O(N)\)
私たちが使用している場合は方法三、我々は使用しているように見える木の違いを
どのように微分法?
各点に関する情報を記録

  • \(TLS [U] \) ルートからの(U \)\、息子ずに残っているどのくらいの合計
  • \(TRS [U] \) ルートからの(U \)\、どのくらい彼の息子の合計は、右の結果ではありません
  • \(TDLS [U] \)ルートからの:\(U \)は見られない合計を残した息子の深さと
  • \(TDRS [U] \) ルートからの\(U \)右の息子の深完全と見ていません

那么我们怎么求以上内容呢?
可以在建树的时候顺便处理出来。
假设我们有一个点 \(fa\),其深度为 \(d\),它的左儿子是 \(lc\),右儿子是 \(rc\)
那么,若 \(rc\) 存在,且 \(lc\) 存在,则满足
\[trs[lc]=trs[fa]+1,tdrs[lc]=tdrs[fa]+(d+1),tls[lc]=tls[fa],tdls[lc]=tdls[fa]\]而若 \(rc\) 不存在,则有
\[trs[lc]=trs[fa],tdrs[lc]=tdrs[fa],tls[lc]=tls[fa],tdls[lc]=tdls[fa]\]如果 \(lc\) 存在,且 \(rc\) 存在,则满足
\[trs[rc]=trs[fa],tdrs[rc]=tdrs[fa],tls[rc]=tls[fa]+1,tdls[rc]=tdls[fa]+(d+1)\]然后,我们就可以在建树,或者是输入建树时预处理出以上内容即可。
接着之前说的树上差分
假若我们要求区间 \([a,b]\) ,那么我们就从 \((a-1,a-1)\)\((b+1,b+1)\) 开始往上爬。
找到它们的最近公共祖先 \(lca\),那么我们要求的公式中的 \(t、\sum_{v\in S[l,r]}d[v]\) 都可以用树上差分解决。
\(t\) 满足
\[t=trs[leafu[a-1]]-trs[lca]+tls[leafu[b+1]]-tls[lca]\]\(\sum_{v\in S[l,r]}d[v]\) 满足
\[\sum_{v\in S[l,r]}d[v]=tdrs[leafu[a-1]]-tdrs[lca]+tdls[leafu[b+1]]-tdls[lca]\]似乎以上部分都是可以使用 \(O(log)\) (寻找 \(lca\))来 \(O(1)\) 地解决问题,但是 \(ans\) 还有一个部分:
\[-\sum_{v\in S[l,r]}2\cdot d[lca(u,v)]\]这个部分能否使用树上差分呢?
答案是:肯定不行。
为什么?因为 \(v\) 在改变时,\(lca(u,v)\) 也在跟着改变。
那么怎么做?
分类讨论 \(u\) 的位置,这里就和我的暴力思路有点相像。
分以下几类:
先假设以 \(leafu[a-1]、leafu[b+1],lca\) 围成的树为 \(tre\)
\(flca()\) 为寻找 \(lca\) 的算法。

  • \(u\)\(tre\) 之外时。这样又要分两类
    - 当 \(u\)\(lca\) 的祖先,那么一定满足\[\forall v\in S[l,r],flca(u,v)=u\]
    - 当 \(u\) 不是 \(lca\) 的祖先,那么一定满足\[\forall v\in S[l,r],flca(u,v)=lca(lca,u)\]
  • \(u\)\(tre\) 之内时,对于这种情况需要自行推理。。。

別のノート:
あなたが成果を話している場合は、見つけることができるが、その後、間隔かどうかを尋ねられた\([1、n]は\ ) があるようなセクションが問題になり、その後、どのように解決するために?実際には、我々は、(例えば、ツリーの対象を)このマップを構築することができ
ここに画像を挿入説明
、コードを

没时间补题啊

おすすめ

転載: www.cnblogs.com/Arextre/p/12210539.html