AtCoderビギナーコンテスト194E-Mex Min

おそらく質問の意味:

1.5×10 6 1.5 \ times10 ^ 6以下のものを与える1 5××1 06 A 1A_1の配列A1 A i A_i A長さ(mm)m、およびnnn、連続する各nnを与えるn個の数に現れない最小の数の中で最小の数。

アイデア:

この質問を見すぐにウィンドウをスライドさせないでください(詳細な説明はここにあります)!もう一方のojを貼り付けた後、変更して直接渡したところ、
ここに画像の説明を挿入
もう一度質問見て、出てこなかった数字の中で一番小さい数字であることに気づきました。データは大きくありません、配列レコードを開く前のnnn個の数字のそれぞれの出現回数と、表示されない数字の中で最小の数字を見つけると、AテールがA_tailになるたびに、ダブルポインターが一方を最後に、もう一方を最後に指します。Aの発生数が1つ減ります、AヘッドA_ヘッドA出現回数に1を加算して、Aの終わりとA_の終わりを決定します。A発生回数が0かどうか、0の場合は、以前に記録した最小値と比較します。現在表示されていない最小値が、以前に表示されていない最小値よりも小さい場合は、以前に表示されていない最小値に入る前にチームにいる必要があります。
このように言いましょう、私の手のより直感的な例を見てみましょう

入力

7 3
2 0 1 2 0 1 0

出力

2
ここに画像の説明を挿入
したがって、出力の答えは2です。これは十分に直感的であり、A i A_iAせいぜい1.5 1 0 6×1.5 \ times10 ^ 61 5××1 06、そしてそのような大きな配列を開くには十分すぎるほどです。

コード

#include <bits/stdc++.h>
using namespace std;
const int N=2e6;
int num[N],a[N],n,k,ans=9999999;
bool flag;
void work()
{
    
    
	int head=k,tail=1;
	for(int i=tail;i<=head;i++)
	{
    
    
		num[a[i]]++;
	}
	for(int i=0;;i++)
	{
    
    	
		if(num[i]==0)
		{
    
    
			ans=min(ans,i);
			if(ans==0)
			{
    
    
				cout<<0;
				flag=1;
				return;
			}
			break;
		}
	}	
	while(head<n)
	{
    
    
		num[a[tail]]--;
		tail++;
		num[a[head]]++;
		if(num[a[tail]]==0)
		{
    
    
			ans=min(ans,a[tail]);
		}
		head++;
	}
}
int main()
{
    
    
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	work();
	if(flag==0)
		cout<<ans;
	return 0;
}

その後、WAには5つのグループの死者と生存者がいて、それを変更することはできませんでした。ゲームをした後、2年生の先輩に送って見てみたところ、2年生と3年生の先輩がそれぞれツリーアレイ、チェアマンツリー、ラインセグメントツリーウォーターを使っていたことがわかりました。ツリー配列のコードは次のとおり
です。理解できないため、説明しません。

#include<bits/stdc++.h>
#define N 1600006
#define LL long long 
#define LB long double
using namespace std;

int n,m;
int ans=1e8,a[N],sp=0;
int mp[N],tr[N];

inline int qr()
{
    
    
	char a=0;int w=1,x=0;
	while(a<'0'||a>'9'){
    
    if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){
    
    x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}

const int K=1500002;

inline void add(int x,int k)
{
    
    
	for(register int i=x;i<=K;i+=i&-i) tr[i]+=k;
}

inline int que(int x)
{
    
    
	int res=0;
	for(register int i=x;i;i-=i&-i) res+=tr[i];
	return res;
}

int jac[123];
inline int ask()
{
    
    
	int pos=1;
	for(register int i=21;i>=0;i--)
		if(pos+jac[i]-1<=K)
		{
    
    
			int op=que(pos+jac[i]-1);
			if(op==pos+jac[i]-1)
				pos+=jac[i];
		}
	return pos;
}

int main()
{
    
    
	n=qr();
	m=qr();
	jac[0]=1;
	for(register int i=1;i<=21;i++) jac[i]=jac[i-1]<<1;
	for(register int i=1;i<=n;i++) a[i]=qr()+1;
	for(register int i=1;i<=n;i++)
	{
    
    
		mp[a[i]]++;
		if(mp[a[i]]==1) add(a[i],1);
		if(i>=m)
		{
    
    
			ans=min(ans,ask());
			mp[a[i-m+1]]--;
			if(!mp[a[i-m+1]]) add(a[i-m+1],-1);
		}
	}
	printf("%d\n",ans-1);
	return 0;
}

この神によって書かれました

次に、コードを変更した後、コードが少し簡略化されました

#include <bits/stdc++.h>
using namespace std;
const int N=2e6;
int num[N],a[N],n,k,ans=9999999;
bool flag;
void work()
{
    
    
	int tou=1,wei=k;
	for(int i=1;i<=k;i++)num[a[i]]++;
	for(int i=0;;i++)
	if(num[i]==0){
    
    ans=i;break;}
	if(ans==0)return;
	while(wei<n){
    
    
		num[a[++wei]]++;
		num[a[tou]]--;
		if(num[a[tou]]==0)
		ans=min(ans,a[tou]);
		tou++;
	}
}
int main()
{
    
    
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	work();
	cout<<ans;
	return 0;
}

実際、主な理由は、テールの終点が早く移動したことです。そのため、最初にテールがa_tailであるかどうかを判断する必要があります。a発生回数が00かどうか0次に、尻尾を動かします。
これはAtCOderのABCで最も簡単なEの質問ですが、試験の間違いで500ポイントを逃しました。ガン、これ前のシミュレーションゲームのT1を思い出させます。メモ化を書くつもりでしたが、xとyを逆にすると、配列が境界を越えました。問題の法的状況の最大の結果、私は法的な状況での最大プロセス額。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/ydsrwex/article/details/114528520