NOIP2020の振り返り

リーグ難易度の共通テンプレート

基本スキル:

ヘッダー ファイルと入出力:

#include<cstdio> //入力および出力ヘッダー ファイル

#include<cstring> //文字列ヘッダー ファイル、配列全体割り当てヘッダー ファイル

#include<cstdlib> //乱数ヘッダファイル

#include<algorithm>//クイック行ヘッダー ファイル

#include<cmath> //sqrt およびその他の数学関数ヘッダー ファイル (abs などは、組み込みのものは機能を超えている可能性があるため、手動で入力することをお勧めします)

#include<ctime> //乱数初期化ヘッダファイル

using namespace std;//アルゴリズム/c で始まらないその他のヘッダー ファイルの場合に入力するもの

文字 str[10];

int main()

{

       int a、b;

       scanf("%d%d",&a,&b);

       printf("%d %d %d\n",a,b,a+b);

長い長い c、d;

       scanf("%lld%lld",&c,&d);

       printf("%lld %lld %lld\n",c,d,c+d);

       scanf("\n%s",str+1); //+1 は最初の位置から開始することを意味することに注意してください (+1 を書かない場合、デフォルトでは 0 番目の位置から開始されます)

       int len=strlen(str+1); //前に+1があり、長さを求めるときも+1が必要

       for (int i=1;i<=len;i++) printf("%c",str[i]); //文字列の出力は文字ごとに出力することを推奨します

       printf("\n");

       ダブルx、y;

       scanf("%lf%lf",&x,&y);

       printf("%.6lf\n",x+y); //小数点以下の扱いに注意

}

配列の全体的な割り当てとクリア:

#include<cstdio>

#include<cstring>//このライブラリを必ず開いてください (マシンを開いていない場合はエラーは報告されませんが、送信するとハングします)

int a[10010],b[10010];

int main()

{

       memset(a,0,sizeof(a)); //全体の割り当て値は 0 です

       memset(a,100,sizeof(a));//全体の割り当てはより大きな値になります

       memcpy(a,b,sizeof(b));//a の全体的な割り当ては、b 配列の各ビットに対応する値です

       0を返します。

}

エッジストレージ方式:

#include<cstdio>

すべて整数;

int las[5010],nxt[10010],to[10010]; //nxt と to のダブルスペース (双方向の場合) に注意し、変数名のスペルを英語で書かないように注意してください (異なるコンパイル環境では簡単に問題を抜け出すことができます)。Las[x] は、番号 x の点が最後に出現したことを示します。Nxt[tot]は現在位置totに対応する点がジャンプする前の位置を示し、to[tot]は現在位置totに対応する点の接続先を示す。x に接続されているすべての点を見つけるには、las[x] から前方にジャンプすることになります。

void 挿入(int x,int y)

{

       nxt[++tot]=las[x];

       las[x]=すべて;

       to[tot]=y;

}

int main()

{

       int m;

       scanf("%d",&m);

       int x,y;

       for (int i=1;i<=m;i++)

       {

              scanf("%d%d",&x,&y);

              insert(x,y); insert(y,x); //双方向エッジ

       }

x=1;

       for (int i=las[x];i;i=nxt[i]) //x に接続されているすべての点を列挙して出力します

       {

              printf("%d\n",to[i]);

       }

}

クイック行:

#include<cstdio>

#include<アルゴリズム>//ヘッダファイル

using namespace std;//必ず追加してください

int a[1010];

bool cmp(int x,int y)

{

       return x>y; //シンボルがより小さい場合は小さいものから大きいものへソートされ、シンボルがより大きい場合は大きいものから小さいものへソートされます。

}

int main()

{

       int n;

       scanf("%d",&n);

       for (int i=1;i<=n;i++) scanf("%d",&a[i]);

       sort(a+1,a+1+n,cmp);//cmp を書かないと、デフォルトで小さいものから大きいものへ

       for (int i=1;i<=n;i++) printf("%d ",a[i]);

       0を返します。

}

構造的な複数キーワードによるクイックソート:

#include<cstdio>

#include<アルゴリズム>

名前空間 std を使用します。

構造体グランド{

       int x,num;     

a[1010];

bool cmp(grand a, grand b)

{

       return (ax<bx) || (ax==bx && a.num<b.num); .x は最初のキーワード、.num は 2 番目のキーワード (小さい順)

}

int main()

{

       int n;

       scanf("%d",&n);

       for (int i=1;i<=n;i++) scanf("%d",&a[i].x),a[i].num=i;

       ソート(a+1,a+1+n,cmp);

       for (int i=1;i<=n;i++) printf("%d ",a[i].x);

       0を返します。

}

終了日:

トピックが複数のデータセットを入力するという場合、ファイルの終わりで終了します。

#include<cstdio>

int main()

{

       int n,x;

       while (scanf("%d",&n)!=EOF)

       {

              scanf("%d",&x);

              printf("%d %d\n",n,x);

       }

       0を返します。

}

湿気/観察/発見のルール:

  1. 疑似貪欲は時には多量の水を必要とすることがある
  2. 数学の問題の解き方がわからない場合は、テーブルをプレイしてルールを見つけてください。
  3. データ範囲が狭い場合は、テーブルを作成してデータベースを構築できます。

乱数: //データ作成時や形而上学プレイ時に使用でき、習得する必要はありません

#include<cstdio>

#include<cstdlib>//専用ヘッダーファイル

#include<ctime>//専用ヘッダーファイル

int main()

{

       srand(time(0)); //ランダムシードの初期化

       int x=rand()%10+1; //このマシンでは rand の範囲は 30,000 以上でなければなりません

       printf("%d\n",x);

       0を返します。

}

グラフ理論:

最短パス (spfa):

var a:倍長整数の配列[1..1000,0..1000];

        f: 倍長整数の配列[1..1000,1..1000];

        dis:配列[1..1000]倍長整数;

        データ:倍長整数の配列[1..100000];

        bz:ブール値の配列[1..1000];

        x、y、t、n、m、i、j、l、r:倍長整数;

始める

        readln(n,m);

        fillchar(f,sizeof(f),10);

        for i:=1 to m do

        始める

                readln(x,y,t);

                f[x,y]<168430090 の場合

                始める

                        f[x,y]>t の場合 f[x,y]:=t;

                そうでなければ

                始める

                        inc(a[x,0]);

                        a[x,a[x,0]]:=y;

                        f[x,y]:=t;

                終わり;

        終わり;

        fillchar(データ,サイズオブ(データ),0);

        fillchar(dis,sizeof(dis),10);

        fillchar(bz,sizeof(bz),false);

        i:=1;

        l:=0;

        r:=1;

        データ[1]:=i;

        dis[i]:=0;

        l<r の間

        始める

                増加(l);

                t:=データ[l];

                for j:=1 to a[t,0] を実行します

                        if dis[t]+f[t,a[t,j]]<dis[a[t,j]]

                        始める

                                dis[a[t,j]]:=dis[t]+f[t,a[t,j]];

                                bz[a[t,j]]=false の場合

                                始める

                                        bz[a[t,j]]:=true;

                                        Inc(r);

                                        データ[r]:=a[t,j];

                                終わり;

                        終わり;

                bz[t]:=false;

        終わり;

        for j:=1 to n を行う

                dis[j]<f[i,j] の場合 f[i,j]:=dis[j];

        for i:=2 to n do writeln(f[1,i]); //これは、開始点として 1 を選択してから他のすべての点までの最短パスです

終わり。

 

最小スパニングツリー:

#include<cstdio>

#include<アルゴリズム>

名前空間 std を使用します。

構造体 zzx{

       int x,y,z;

a[10010];

int fa[10010];

bool cmp(zzx a,zzx b)

{

       az<bz; を返します。      

}

int 父(int x)

{

       if (x==fa[x]) は x を返します。

       fa[x]=父親(fa[x]);

}

int main()

{

       int n,m;

       scanf("%d%d",&n,&m);

       for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);

       ソート(a+1,a+1+m,cmp);

       for (int i=1;i<=n;i++) fa[i]=i;

       int s=0;

       for (int i=1;i<=m;i++)

       {

              int FAX=父(a[i].x);

              int fay=father(a[i].y);

              if (ファックス!=フェイ)

              {

                     s=s+a[i].z;

                     fa[fax]=フェイ;

              }

       }

       printf("%d\n",s);

}

 

LCA:

//fa は各ポイントの親ノードを表し、deep は各ポイントの深さを表します

int fa[100],deep[100];

int LCA(int a,int b)

{

       //関数内で、a の深さが b の深さよりも大きいことを確認します。これは、後の操作に便利です。

       if(ディープ[a]<ディープ[b])

              スワップ(a,b);

       // b を a と同じ深さになるまで継続的に親ノードにジャンプさせます。

       while(ディープ[a]>ディープ[b])

              b=深い[b];

       // a と b が出会うまで同時に飛び上がります。

       while(a!=b)

       {

              a=深い[a];

              b=深い[b];

       }

       を返します。

}

倍加:

https://blog.csdn.net/Q_M_X_D_D_/article/details/89924963 (説明付き)

//fa は各ポイントの親ノードを表します

int fa[100],DP[100][20];

void init()

{

       //n はノードの数です。最初に DP 配列を初期化します

       for(int i=1;i<=n;i++)

              dp[i][0]=fa[i];

       // DP 配列全体を見つけるための動的プログラミング

       for(int j=1;(1<<j)<=n;j++)

              for(int i=1;i<=n;i++)

                     DP[i][j]=DP[DP[i][j-1]][j-1];

}

//クエリ関数

int LCA(int a,int b)

{

    // a の深さが b より大きいことを確認します。これは、後の操作に便利です。

       if(dep[a]<dep[b])

              スワップ(a,b);

    // a が b と同じ深さになるまでジャンプし続けます。

    // a の深さが b よりも大きいことが保証できない場合、このステップで a と b のどちらが飛び上がっているかを判断することは不可能です

       for(int i=19;i>=0;i--)

       {

        // ジャンプアップは深さを減らすプロセスです

              if(dep[a]-(1<<i)>=dep[b])

                     a=dp[a][i];

       }

    // 2 つが同じ深さにあり、正確に一致する場合、この点は LCA です

       if(a==b)

              を返します。

    //a と b は同時にジャンプし、大きいステップ サイズから小さいステップ サイズに移動し、適切なステップ サイズに達したらジャンプし、適切でない場合はステップ サイズを減らします。

       for(int i=19;i>=0;i--)

       {

        // 二人が会わない場合は飛び上がる

              if(dp[a][i]!=dp[b][i])

              {

                     a=dp[a][i];

                     b=dp[b][i];

              }

       }

    //最後に、a と b は LCA の次の層にジャンプし、LCA は a と b の親ノードになります

       dp[a][0] を返します。

}

 

違い:

序列差分:https://gmoj.net/junior/#main/show/2162

長方形の場合、4つの頂点位置の値の加減算により、長方形内のすべての位置の加減算が実現されます。接頭辞と を使用します。

var n,q,x,y,i,j,x1,y1,x2,y2:longint;

        a: 倍長整数の配列[1..3001,1..3001];

        sum: 倍長整数の配列[0..3000,0..3000];

始める

        assign(input,'square.in');reset(input);

        assign(output,'square.out');rewrite(output);

        readln(n);

        for i:=1 to n を行う

        始める

                readln(x1,y1,x2,y2);

                inc(a[x1,y1]);

                dec(a[x1,y2+1]);

                dec(a[x2+1,y1]);

                inc(a[x2+1,y2+1]);

        終わり;

        for i:=1 ~ 3000 を実行します

                for j:=1 ~ 3000 を実行します

                        sum[i,j]:=sum[i-1,j]+sum[i,j-1]-sum[i-1,j-1]+a[i,j] ;

        readln(q);

        for i:=1 to q do

        始める

                readln(x,y);

                writeln(sum[x,y]);

        終わり;

        閉じる(入力);閉じる(出力);

終わり。

木の違い:

ツリー上の 2 つの点 x と y は、x から y へのパス上のすべてのエッジ + a を作成します。次に、x + a の親側、y + a の親側、および lca -2a の親側を置きます。エッジのサブツリーの合計をクエリすることにより、エッジの値をクエリします。

 

ツリー上の 2 つの点 x、y は、x から y へのパス上のすべての点 + a を作成します。次に、x + a の値、y + a の値、lca の値 - a、および lca の父親の値 - a を入力します。サブツリーの合計をクエリすることで、ポイントの値をクエリします。

 

 

二部グラフマッチング (ハンガリーアルゴリズム):

https://blog.csdn.net/sunny_hun/article/details/80627351 (説明付き)

#include<cstdio>

#include<cstring>

int a[1010][1010];

int f[1010];

bool bz[1010];

ブール pd(int x)

{

       for (int i=1;i<=a[x][0];i++)

              if (!bz[a[x][i]])

              {

                     bz[a[x][i]]=true;

                     if (f[a[x][i]]==0 || pd(f[a[x][i]]))     

                     {

                            f[a[x][i]]=x;

                            true を返します。

                     }

              }

       false を返します。

}

int main()

{

       int n,m,e;

       scanf("%d%d%d",&n,&m,&e);

       int x,y;

       for (int i=1;i<=e;i++)

       {

              scanf("%d%d",&x,&y);

              if (y>m) 続行;

              a[x][++a[x][0]]=y;

       }

       int ans=0;

       for (int i=1;i<=n;i++)

       {

              memset(bz,0,sizeof(bz));

              if (pd(i)) ans++;

       }

       printf("%d\n",ans);

       0を返します。

}

 

一般的な期待値の計算:

3 つの点、2 つの隣接する点は双方向エッジで接続されます。

 

f[i] が点 1 から点 i までの最初の予想ステップ数を表すものとします。

この場合、f[1]=0、f[2]=1となります。次に f[3] を求めます。

最初はポイント 1 からポイント 2 に進むことだけを考えてください。ポイント 2 では、それぞれ 1/2 の確率でポイント 1 に戻るか、ポイント 3 に進むことができます。3番まで行くと終了です。1番に戻ると最初から始めるのと同じです。

方程式思考を使用すると、次のようになります。

f[3]=1/2*(f[2]+1)+1/2*(f[2]+1+f[3])、前半はポイント 3 に進み、後半はポイント 1 に戻ってやり直します。解は f[3]=4 です。

ツリー上の期待値の計算でも、同様の方程式の考え方を使用できます。

 

ツリーチェーンの分割:

https://blog.csdn.net/HiChocolate/article/details/77170675 (テンプレート、取り扱い)

木のエッジを軽いエッジと重いエッジに分割します。ツリーの各レベルには、多重エッジが 1 つだけあります。これは、接続された息子の最も多くの子孫 (最大のサブツリー サイズ) を持つエッジです。崇辺と関係のある息子は崇子と呼ばれます。

ツリーチェーンを分割するプロセスは 2 dfs です

初回: 定義に従って、重いエッジ、重い息子を見つける

2回目:重いエッジを優先する原則に従って、dfsシーケンスを作成し、dfsシーケンス内の点xの位置をtree[x]として記録し、各点が属する重鎖の開始点top[x]を計算します。

分割後の各重鎖はセクションに相当し、データ構造 (線分ツリーなど) によって維持されます。

すべての重鎖を端から端まで接続し、同じデータ構造上に置き、この全体を維持します。

u と v が同じ重鎖上にある場合は、データ構造を直接使用して、tree[u] と Tree[v] の間の値を変更するか、答えをクエリします。

u と v が同じ重鎖上にない場合、修飾中に u と v を同じ重鎖上に置くと、上記の状況になります。

具体的な操作: パーティ x をより深くその fa[top[x]] にジャンプし、tree[top[x]]~tree[x] を変更またはクエリします。

重鎖はデータ構造内の連続した区間であるため、tree[top[x]]~tree[x] を直接クエリしても問題ありません。

数論:

Gcd:

#include<cstdio>

int gcd(int x,int y)

{

       if (x%y==0) は y を返します。それ以外の場合は gcd(y,x%y) を返します。

}

int main()

{

       int a、b;

       scanf("%d%d",&a,&b);

       printf("%d\n",gcd(a,b));

       0を返します。

}

クイックパワー:

#include<cstdio>

長い長いミ(長い長いa、長い長いb)

{

       長い長い s=1;

       一方 (b)

       {

              if (b&1) s=s*a;

              a=a*a;

              b/=2;

       }

       戻り値;

}

int main()

{

       int n,m;

       scanf("%d%d",&n,&m);

       長い長い s=mi(n,m);

       printf("%lld\n",s);

       0を返します。

}

%p の意味で数値の逆数を求めます。

p が素数の場合、数値を x で割ることは、x^(p-2) を掛けることと同じです。このようにして、モジュール的な意味での除算演算が実現されます。

p が素数でない場合、逆元は存在しないため、このとき除算演算は避けるべきです。

 

素数ふるい:

#include<cstdio>

bool bz[100010];

int zhi[100010];

int main()

{

       int n;

       scanf("%d",&n);

       for (int i=3;i*i<=n;i+=2)

              if (!bz[i])

                     for (int j=i;i*j<=n;j+=2) bz[i*j]=true; //bz は偶数をマークしないことに注意してください

       zhi[0]=1; zhi[1]=2;

       for (int i=3;i<=n;i+=2)

              if (!bz[i]) zhi[++zhi[0]]=i;

       for (int i=1;i<=zhi[0];i++) printf("%d ",zhi[i]);

       0を返します。

}

 

例外:

https://www.cnblogs.com/mrclr/p/9380300.html

exgcd を使用すると、x、y のセットに対する方程式 ax +by = gcd(a, b) を解くことができます。

int exgcd(int a,int b,int &x,int &y) //注意x,y前的&

{

       int ;

       もし (!b)

       {

              s=a、x=1、y=0;

              戻り値;

       }

       s=exgcd(b,a%b,y,x); y-=a/b*x;

       戻り値;

}

 

ウミガメの乗り物:

2 つの数値 a、b があり、a*b%p の結果が必要です。

a と b がlong long を超えないが、それらを掛けると超えてしまう場合はどうなるでしょうか?

a*b を b に変換し、a を加算すると、高速べき乗と同様に、各乗算のみが加算になります。

このように少しずつ加算していくので加算するたびに剰余が取られるので爆発的に長くなることはありません

https://blog.csdn.net/jz_terry/article/details/86670193

ロングロングチェン(ロングロングa、ロングロングb)

{

       長い長い s=0;

       一方 (b)

       {

              if (b&1) s=(s+a)%Mo;

              a=(a+a)%I;

              b>>=1;

       }

       戻り値;

}

 

%p の意味で 1~n の逆数を線形に求めます:再帰、f[i]=f[p%i]*(pp/i)%p  

導出: p=a*i+b とすると、a*i+b 0 (%p) となります。i の逆数が必要なため、式は i^-1=.... の形式に変換する必要があります。つまり、1/i=-a/b=-y/x*(y%x)^-1、負の数を避けるために、p も追加する必要があります。したがって、上記の再帰式を取得します。

 

1~n の各数値 i の階乗 i を線形計算します。f[i]=f[i+1]*(i+1)%pの逆数。

証明: i!^-1=(i+1)!^-1)(i+1) ((i+1) を掛けると (i+1)^-1 が消去され、n から後ろに押し戻されるようになります)

DP

ナップザック問題の dp:

01 バックパック:

アイテムが t 個あり、容量 n のナップザックがあります。i 番目の項目は 1 つだけあり、ボリュームは v[i]、値は w[i] です。バックパックに積むアイテムの体積の合計がバックパックの容量を超えないように選択し、その値の合計が最大となる値を求めます。

var i,j,t,n:longint;

        f: 倍長整数の配列[0..1000,0..1000];

        w,v:倍長整数の配列[1..1000];

関数 max(a,b:longint):longint;

始める

        if a>b then exit(a) else exit(b);

終わり;

始める

        readln(n,t); //tは項目数、nは容量数

        for i:=1 to t do

                readln(w[i],v[i]);

        for i:=1 to t do

                for j:=1 to n を行う

                        j>=w[i] の場合

                        f[i,j]:=max(f[i-1,j],f[i-1,jw[i]]+v[i]) else

                        f[i,j]:=f[i-1,j];

        writeln(f[t,n]);

終わり。

バックパック一式:

アイテムが t 個あり、容量 n のナップザックがあります。i 番目のアイテムは無限にあり、ボリュームは v[i]、値は w[i] です。
バックパックに積むアイテムの体積の合計がバックパックの容量を超えないように選択し、その値の合計が最大となる値を求めます。

var w,v:倍長整数の配列[0..10000];

        f: 倍長整数の配列[0..1000,0..10000];

        i,j,n,m,k,l,t:倍長整数;

関数 max(x,y:longint):longint;

始める

        if x>y then exit(x) else exit(y);

終わり;

始める

        readln(n,t);

        for i:=1 to t do readln(w[i],v[i]);

        for i:=1 to t do

        始める

                for j:=1 to n を行う

                始める

                         for k:=0 to j div w[i] do

                         始める

                                w[i]*k>j の場合 f[i,j]:=f[i-1,j]

                                else f[i,j]:=max(f[i,j],f[i-1,jw[i]*k]+v[i]*k)

                         終わり;

                終わり;

        終わり;

        write(f[t,n]);

終わり。

 

最長の非減少サブシーケンスの dp:

b[i] を最長の非減少サブシーケンスの i 番目のビットの最小エネルギーを表すものとします (i 番目のビットが i-1 番目のビット以上であることを満たすため)

新しい数値が追加されるたびに、部分列を増やせる場合は増分し、増やせない場合は b を更新できるかどうかを確認します。

var n,i,j:longint;

        a,b:倍長整数の配列[0..100];

始める

        readln(n);

        for i:=1 to n do read(a[i]);

        b[0]:=0;

        for i:=1 to n を行う

        始める

                a[i]>=b[b[0]] の場合

                始める

                        inc(b[0]);

                        b[b[0]]:=a[i];

                そうでなければ

                始める

                        for j:=b[0]-1 downto 1 do //この部分を二分法に変えると、複雑さはnlognになります。

                                a[i]>=b[j] の場合

                                始める

                                        b[j+1]:=a[i];

                                        壊す;

                                終わり;

                        b[1]>a[i] の場合、b[1]:=a[i];

                終わり;

        終わり;

        writeln(b[0]);

終わり。

 

ツリー dp:

n 個のポイントを持つツリー。各ポイントには重み (正または負) があり、その重みとツリー内の最大のサブツリーを見つけて、最大の重みを出力します。

#include<cstdio>

すべて整数;

int nxt[2010],to[2010],las[1010];

int f[1010];

int a[1010];

void 挿入(int x,int y)

{

       nxt[++tot]=las[x];

       las[x]=すべて;

       to[tot]=y;

}

void dfs(int x,int fa)

{

       f[x]=a[x];

       for (int i=las[x];i;i=nxt[i])

              if (to[i]!=fa)

              {

                     dfs(to[i],x);

                     f[x]=f[x]+f[to[i]];

              }

}

int main()

{

       int n;

       scanf("%d",&n);

       for (int i=1;i<=n-1;i++)

       {

              int x,y;

              挿入(x,y);

              挿入(y,x);

       }

       for (int i=1;i<=n;i++) scanf("%d",&a[i]);

       dfs(1,0);

       int mx=0;

       for (int i=1;i<=n;i++)

              if (f[i]>mx) mx=f[i];

       printf("%d\n",mx);

}

 

単調なキュー:

要素をキャッシュ配列に継続的に読み取り、最も古い要素を時々削除し、現在のキャッシュ配列内の最小の要素を時々尋ねます。

https://blog.csdn.net/ljd201724114126/article/details/80663855

 

傾斜の最適化:

https://blog.csdn.net/jz_terry/article/details/103212006

他の:

2 つのポイント: (ポットから抜け出すのは簡単です。プレイ後に数回デバッグまたはシミュレーションすることをお勧めします)

小さいものから大きいものへと並べ替えられたシーケンスで、x より大きい最初の数値の位置を見つけます。

#include<cstdio>

int a[1010];

int main()

{

       int n,x;

       scanf("%d",&n);

       for (int i=1;i<=n;i++) scanf("%d",&a[i]);

       scanf("%d",&x);

       int l=1; int r=n+1; //rは条件を満たす最小の位置を表すため、初期値をnにすることはできません(a[n]がxより大きいとは限りません)

       一方 (l<r)

       {

              int Mid=(l+r)/2;

              if (a[mid]<=x) l=mid+1; それ以外の場合は r=mid;

       }

       printf("%d\n",r);

       0を返します。

}

小さいものから大きいものへと並べ替えられたシーケンスで、x より小さい最後の数値の位置を見つけます。

#include<cstdio>

int a[1010];

int main()

{

       int n,x;

       scanf("%d",&n);

       for (int i=1;i<=n;i++) scanf("%d",&a[i]);

       scanf("%d",&x);

       int l=1; int r=n; //定義 l-1 は満たされますが、r+1 は満たされません

       while (l<=r) //前のものとは異なり、l=r の場合にも l がいっぱいかどうかを判断する必要があります。

       {

              int Mid=(l+r)/2;

              if (a[mid]>=x) r=mid-1; それ以外の場合は l=mid+1;

       }

       printf("%d\n",l-1);

       0を返します。

}

 

セグメントツリー:

単一点変更、間隔クエリ:

#include<cstdio>

#include<cstdlib>

#include<iostream>

名前空間 std を使用します。

int n、m、k、x、y、ans;

int a[100010];

int ツリー[400010];

void maketree(int x,int st,int en)

{

       int m;

       if (st==en) ツリー[x]=a[st];

       それ以外

       {

              m=(st+en)>>1;

              maketree(x+x,st,m);

              maketree(x+x+1,m+1,en);

              ツリー[x]=max(ツリー[x+x],ツリー[x+x+1]);

       }

}

void change(int x,int st,int en,int p,int value)

{

       int m;

       if (st==en) ツリー[x]=値;

       それ以外

       {

              m=(st+en)>>1;

              if (p<=m)

              {

                     変更(x+x,st,m,p,値);

              }

              それ以外の場合は、change(x+x+1,m+1,en,p,value);

              ツリー[x]=max(ツリー[x+x],ツリー[x+x+1]);

       }

}

void find(int x,int st,int en,int l,int r)

{

       int m;

       if (l==st && r==en) ans=max(tree[x],ans);

       それ以外

       {

              m=(st+en)>>1;

              if (r<=m) find(x+x,st,m,l,r);

              else if (l>m) find(x+x+1,m+1,en,l,r);

              それ以外

              {

                     find(x+x,st,m,l,m);

                     find(x+x+1,m+1,en,m+1,r);

              }

       }

}

int main()

{

       scanf("%d",&n);

       for (int i=1;i<=n;i++)

       {

              scanf("%d",&a[i]);

       }

       maketree(1,1,n);

       scanf("%d",&m);

       for (int i=1;i<=m;i++)

       {

              scanf("%d%d%d",&k,&x,&y);

              if (k==1)

              {

                     変更(1,1,n,x,y);

              }

              それ以外の場合 (k==2)

              {

                     ans=0;

                     find(1,1,n,x,y);

                     printf("%d\n",ans);

              }

       }

}

間隔の変更、間隔のクエリ:

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<iostream>

名前空間 std を使用します。

長い長い f[300010];

int a[300010];

int g[300010];

長い長いですね。

int n,m;

int x、l、r、c;

void maketree(int x,int st,int en)

{

       int ミッド;

       if (st==en) f[x]=a[st];

       それ以外

       {

              ミッド=(st+one)/2;

              maketree(x*2,st,mid);

              maketree(x*2+1,mid+1,en);

              f[x]=max(f[x*2],f[x*2+1]);     

       }

}

void update(int x,int st,int en,int l,int r,int v)

{

       int ミッド;

       if (st==l && en==r)

       {

              f[x]=f[x]+v;

              g[x]=g[x]+v;

       }

       それ以外

       {

              f[x*2]+=g[x];

              f[x*2+1]+=g[x];

              g[x*2]+=g[x];

              g[x*2+1]+=g[x];

              g[x]=0;

              ミッド=(st+one)/2;

              if (r<=mid) update(x*2,st,mid,l,r,v);

              else if (l>mid) update(x*2+1,mid+1,en,l,r,v);

              それ以外

              {

                     update(x*2,st,mid,l,mid,v);

                     update(x*2+1,mid+1,en,mid+1,r,v);

              }

              f[x]=max(f[x*2],f[x*2+1]);

       }

}

void getmax(int x,int st,int en,int l,int r)

{

       int ミッド;

       if (st==l && en==r)

       {

              ans=max(ans,f[x]);

       }

       それ以外

       {

              //

              f[x*2]+=g[x];

              f[x*2+1]+=g[x];

              g[x*2]+=g[x];

              g[x*2+1]+=g[x];

              g[x]=0;

              //

              ミッド=(st+one)/2;

              if (r<=mid) getmax(x*2,st,mid,l,r);

              else if (l>mid) getmax(x*2+1,mid+1,en,l,r);

              それ以外

              {

                     getmax(x*2,st,mid,l,mid);

                     getmax(x*2+1,mid+1,en,mid+1,r);

              }

       }

}

int main()

{

       scanf("%d",&n);

       for (int i=1;i<=n;i++)

       {

              scanf("%d",&a[i]);

       }

       maketree(1,1,n);

       scanf("%d",&m);

       for (int i=1;i<=m;i++)

       {

              scanf("%d",&x);

              if (x==1)

              {

                     scanf("%d%d%d",&l,&r,&c);

                     更新(1,1,n,l,r,c);

              }

              それ以外

              {

                     ans=-2147483647000;

                     scanf("%d%d",&l,&r);

                     getmax(1,1,n,l,r);

                     printf("%d\n",ans);

              }

       }

       //システム("一時停止");

       0を返します。

}

ツリー配列:

https://blog.csdn.net/jz_terry/article/details/78543825

记得two[i]=i&(-i);

単一ポイント変更、間隔クエリ

#include<cstdio>

int a[100001];

int 2[100001];

int c[100001];

int sum[100001];

チャーch;

int qiuhe(int x)

{

       int s=0;

       一方 (x>0)

       {

              s+=c[x];

              x=x-2[x];

       }

       戻り値;

}

int main()

{

       int n,m;

       scanf("%d",&n);

       int i;

       for (i=1;i<=n;i++)

       {

              scanf("%d",&a[i]);

              sum[i]=sum[i-1]+a[i];

       }

       for (i=1;i<=n;i++) two[i]=i&(-i);

       for (i=1;i<=n;i++)

              c[i]=sum[i]-sum[i-two[i]];

       scanf("%d",&m);

       int x、y、k、t;

       for (i=1;i<=m;i++)

       {

              scanf("\n%c%d%d",&ch,&x,&y);

              if (ch=='C')

              {

                     k=x;

                     t=a[x];

                     a[x]=y;

                     一方 (k<=n)

                     {

                            c[k]=c[k]-t+y;

                            k=k+2[k];

                     }

              } それ以外

              {

                     printf("%d\n",qiuhe(y)-qiuhe(x-1));                   

              }

       }

       0を返します。

}

KMP:

#include<cstdio>

文字 a[100001];

char b[100001];

int p[100001];

int f[100001];

int main()

{

       int n,m;

       scanf("%d%d",&n,&m);

       scanf("%s",a+1);

       scanf("%s",b+1);

       int i,j;

       for (i=2;i<=m;i++)

       {

              j=p[i-1]; //p[i] は、一致する文字列の i 番目の桁で終わるサフィックス、つまり ip[i]+1~i を示し、1~p[i] と同じです。上記の条件で p[i] を最大にする。

              while (j>0 && b[j+1]!=b[i]) j=p[j]; //最初に p[i-1] を使用し続け、その後、もう 1 ビットを満たす必要があるため、不一致が続き、調整と削減が続きます。

              if (b[j+1]==b[i]) p[i]=j+1; // 正当であることが判明した場合は更新、見つからない場合は p[i] が 0

       }

       for (i=1;i<=n;i++)

       {

              j=f[i-1];//f[i] は、元の文字列の i 番目のビットが接尾辞であることを意味し、元の文字列の if[i]+1~i は、一致した文字列の 1~f[i] と同じであり、f[i] が最大になります。

              while (j>0 && b[j+1]!=a[i]) j=p[j]; 最初に f[i-1] を使用し、次にもう 1 ビットを満たす必要があるため、不一致が続き、調整と削減が繰り返されます。

              if (b[j+1]==a[i])

              {

                     f[i]=j+1;

                     if (f[i]==m)

                     {

                            printf("%d\n",i-m+1);

                            f[i]=p[f[i]]; //完全一致を見つけた後、次の一致を探し続けるために、f[i] は 1 ステップ戻る必要があります。

                     }

              }

       }

       0を返します。

}

最小ヒープ:

https://blog.csdn.net/hrn1216/article/details/51465270 (原則を忘れた場合)

ヒープソート

#include<cstdio>

#include<cstring>

#include<cstdlib>

名前空間 std を使用します。

int g[300010],i,n,a;

ボイドアップ(int t)

{

       int q;

       if(t==1)return;

       q=t/2;

       if(g[q]>g[t])

       {

              a=g[q]; g[q]=g[t]; g[t]=a;

              アップ(q);

       }

}

ボイドダウン(int t)

{

       int p;

       if(t*2>n)return;

       p=t*2;

       if ((p<n)&&(g[p+1]<g[p]))p++;

       if(g[p]<g[t])

       {

              a=g[p]; g[p]=g[t]; g[t]=a;

              ダウン(p);

       }     

}

int main()

{

       scanf("%d",&n);

       for(i=1;i<=n;i++)

       {

              scanf("%d",&g[i]);

              アップ(i);

       }

       for(;n>1;)

       {

              printf("%d\n",g[1]);

              g[1]=g[n];

              ん--;

              ダウン(1);

       }

       printf("%d\n",g[1]);

}

 

ハッシュ:

各ビットに異なる係数を乗算して法を取得することにより、文字列や大きな数値を比較的小さな数値に変換し (数値を与えるのと同じ)、配列内の数値の位置に格納しようとします。配列内の数値の添字がすでに格納されており、元の文字列が異なる場合、空きが見つかって挿入されるまで、数値は徐々に累積されます。

 

馬車:

https://blog.csdn.net/Gao_Jue_Yi/article/details/81435328 (以前のブログ)

#include<cstdio>

#include<cstring>

文字 a[11000010];

char b[22000010];

int p[22000010];

int main()

{

       scanf("%s",a+1);

       int n=strlen(a+1);

       b[1]='*';

       int m=1;

       for (int i=1;i<=n;i++)

              b[++m]=a[i],b[++m]='*';

       int x=0; int y=0;

       int ans=0;

       for (int i=1;i<=m;i++)

       {

              if (y>i)

              {

                     if (p[x*2-i]+i-1<=y) p[i]=p[x*2-i]; それ以外の場合、p[i]=y-i+1;

              それ以外の場合は p[i]=1;

              while (ip[i]>=1 && i+p[i]<=m && b[ip[i]]==b[i+p[i]]) p[i]++;

              if (y<i+p[i]-1)

              {

                     y=i+p[i]-1;

                     x=i; 

              }

              if (p[i]-1>ans) ans=p[i]-1;

       }

       printf("%d\n",ans);

       0を返します。

}

 

ネットワーク ストリーム: (リーグの範囲に属してはなりません。マスターする必要はありません)

#include<cstdio>

#include<cstring>

int n、m、S、T;

int las[10010],nxt[200010],to[200010];

long long num[200010];

int ギャップ[10010],dis[10010];

int all=-1;

int min(longlong x,longlong y)

{

       (x<y) の場合は x を返します。それ以外の場合は y を返します。

}

void insert(int x,int y,long long z)

{

       nxt[++tot]=las[x];

       las[x]=すべて;

       to[tot]=y;

       数値[すべて]=z;

}

long long dfs(int x,long long s)

{

       if (x==T) return s; //すでにシンクに流れているので、フローを返す

       int have=0; //シンク T に流入したトラフィックの量

       for (int i=las[x];i!=-1;i=nxt[i])

              if (dis[to[i]]+1==dis[x] && num[i]>0) //距離ラベル、増加するトラフィックは最短パスのプロパティを満たす必要があり、この側には値があります

              {

                     int now=dfs(to[i],min(s-have,num[i]));

                     num[i]-=now; num[i^1]+=now; //i^1 は to[i]->x の逆エッジ番号であり、エッジを保存することで実現されます

                     have+=now; //ストレージトラフィックを更新します

                     if (have==s) return s; // フローがすでにいっぱいの場合は、不可能なフローによって距離ラベルが変更されるのを防ぐために直接戻ります

              }

       if (--gap[dis[x]]==0) dis[1]=n; //距離ラベルに隙間がある場合、再度流れることは不可能

       gap[++dis[x]]++;//距離ラベルを更新します

       持っているものを返します。

}

int main()

{

       scanf("%d%d%d%d",&n,&m,&S,&T);

       memset(las,255,sizeof(las));

       memset(nxt,255,sizeof(nxt));

       for (int i=1;i<=m;i++)

       {

              int x,y; 長い長いz。

              scanf("%d%d%lld",&x,&y,&z);

              挿入(x,y,z);

              挿入(y,x,0);   

       }

       長い長い合計=0;

       ギャップ[0]=n;

       while (dis[S]<n) sum+=dfs(S,9000000000000000LL);

       printf("%lld\n",sum);

       0を返します。

}

 

日課とスキルと注意事項:

  1. [l..r] の解の数は通常、[1..r]-[1..l-1] に変換されます。
  2. インターバルの補償に関しては、その違いについて考えてみましょう。
  3. 最小値と最大値を求めるときは二分法を考えてください

4. 不可能の数 = 総数 - 可能の数。

5. C++ はエラーを報告することがほとんどないため、配列が境界を越えるかどうか、およびロングロングをオープンするかどうかに注意する必要があります。

6. 2 点をプレイする場合、数回のデバッグが必要です。

7. 時間配分に注意し、予想外の暴力には対処してください。

8. タイムアウトしたと思われる一部の検索は、枝刈りを追加すると多くのポイントを獲得したり、切断されたりする可能性があるため、最大値と最小値の枝刈り、合法性枝刈り、記憶など、検索を可能な限り枝刈りする必要があります。

9. データ範囲内の奇妙な特殊データに注意してください。

10. C++ におけるビット演算の優先順位は非常に低いため、括弧を追加することをお勧めします。

11. 問題を解くには、問題を何度も読む必要があり、問題を解くには、まず例題を理解する必要があります。

12. モデル化が必要な一部の質問については、モデルが非常に遅いため、行き詰まったときに数回ごとにモデルにアクセスできます。

13. 配列の呼び出しも比較的遅く、少なくとも変数を直接使用するよりも遅いため、変数を頻繁に使用できる場合は配列を呼び出さないでください。

14. 忘れずに初期化してください。

15. スペースが許す限り、long long が消費するスペースをケチらず、値が比較的大きくなりそうな場合は、long long を使用してください。

16. 実数型に遭遇した場合は、精度が固定されないように、double (パスカルの拡張) を直接使用します。

17. 線分のツリー空間は 4n である必要があります。つまり、100,000 の数値を 400,000 のスペースに保存する必要があります。

18. データが非常に大きい場合、O(1) でしか合格できない問題は、一般的に公式結論問題であるようです。公式を推測できない場合は、暴力を使って法則を見つけることができます。

19. Sqrt は cmath を開く必要があります。コンパイラ上でアルゴリズムを開くだけで渡せますが、サブミットするとエラーになります。

20. cmath ライブラリを開くときに、get、last、next などの英語で綴られた変数名を使用しないでください。また、x0、y0、x1、y1 などを使用しないでください。コンパイル環境が異なるとコンパイルエラーが発生する可能性があるためです。

 

 

おすすめ

転載: blog.csdn.net/jz_terry/article/details/109458507
おすすめ