天天看點

3.27模拟賽

(挂慘了 我晚上一定好好睡覺。

T1 求冒泡排列在第幾輪結束。\(n\leq 3e7\)

可以發現每次自己左邊最大的數字是向右移動的 是以答案為max{自己左邊有多少個數比自己大}

想要O(n)求出的話還要再考慮一下 如何求出max 由于是max 我們考慮自己右邊最多有多少比自己小的max等價于原答案。

是以我們可以發現 利用較小的數字來統計答案會比用較大的數字統計答案更優 換個說法 \(a_i<a_j\) \(a_i\)和\(a_j\)如果進行換位了 那麼我們隻統計\(a_i\)的對答案的

貢獻至少不會比\(a_j\)要小。一個很難得到最很好好證明的答案:max{i-\(a_i\)}即為答案。

考慮證明:一個數字前面包括本身最多有\(a_i\)個數對答案沒有貢獻。考慮如果不夠這\(a_i\)個數 那麼一定有比自己還要小的數字在自己後面 那麼這樣統計下去至少能夠找到答案。

bf:
const int MAXN=30000010;
int n;
ll S,B,C,D;
int a[MAXN],c[MAXN];
int cnt,ans;
int main()
{
	freopen("bubble.in","r",stdin);
	freopen("bubble.out","w",stdout);
	get(n);get(S);get(B);get(C);get(D);
	rep(1,n,i)
	{
		a[i]=i;
		S=(S*B+C)%D;
		swap(a[i],a[(S%i)+1]);
	}
	//rep(1,n,i)cout<<a[i]<<' '<<endl;
	rep(1,n,i)
	{
		int w=a[i];cnt=0;
		while(w)
		{
			cnt+=c[w];
			w-=w&(-w);
		}
		ans=max(ans,i-1-cnt);
		w=a[i];
		while(w<=n)
		{
			++c[w];
			w+=w&(-w);
		}
	}
	put(ans);
	return 0;
}
sol:

const int MAXN=30000010;
int n;
ll S,B,C,D;
int a[MAXN],c[MAXN];
int cnt,ans;
int main()
{
	freopen("1.in","r",stdin);
	get(n);get(S);get(B);get(C);get(D);
	rep(1,n,i)
	{
		a[i]=i;
		S=(S*B+C)%D;
		swap(a[i],a[(S%i)+1]);
	}
	//rep(1,n,i)cout<<a[i]<<' '<<endl;
	rep(1,n,i)ans=max(ans,i-a[i]);
	put(ans);
	return 0;
}
           

T2:

3.27模拟賽
3.27模拟賽

sb比對題...無語。(每次我都暴力 每次暴力還挂。我真是個菜雞。

容易推出性質 兩個比對的字元串a,b a種某種字元隻能對應b上的位置隻能對應一種字元 b種也同理 容易證明這樣是可以比對的充要條件。

可以簡單證明一下:如果滿足上述條件 那麼直接swap即可 可以發現這樣做就可以形成一模一樣的字元。考慮必要條件的證明:反證:如果不滿足上述情況 那麼必然某種字元對應了兩種字元 而swap每次隻能将一種字元變成另外一種字元 如果swap是那兩種字元 顯然無法比對還是原來的情況 如果不是 顯然還是原情況。證畢。

是以現在問題變成了 給出兩個字元串看 是否滿足上述條件。考慮暴力 n^2 比對。最尴尬的事實是優化不了 這個思路雖然沒錯 但是 必須一一得到位置資訊。

貼一發暴力代碼:

const int MAXN=1000010;
int T,C,n,m,top;
int ans[MAXN];
int a[MAXN],b[MAXN],c[MAXN],c1[MAXN],cnt,cnt1;
vector<int>w[MAXN];
int main()
{
	freopen("1.in","r",stdin);
	//freopen("match.out","w",stdout);
	get(T);get(C);
	while(T--)
	{
		get(n);get(m);top=0;
		rep(1,C,i)w[i].clear();
		rep(1,n,i)get(a[i]);
		rep(1,m,i)get(b[i]),w[b[i]].pb(i);
		rep(1,n-m+1,i)
		{
			int cnt=0;
			rep(1,C,j)
			{
				if(w[j].size())
				{
					int last=0;
					rep(0,w[j].size()-1,k)
					{
						int ww=w[j][k];
						if(last&&last!=a[i+ww-1]){cnt=1;break;}
						last=a[i+ww-1];
						if(c[last]&&c[last]!=j){cnt=1;break;}
						c[last]=j;
					}
					if(cnt)break;
				}
			}
			rep(1,C,j)c[j]=0;
			if(!cnt)ans[++top]=i;
		}
		put(top);
		rep(1,top,i)printf("%d ",ans[i]);
		puts("");
	}
	return 0;
}
           

可以發現這是一個sb比對題 我們可以考慮算法了 hash 或者 KMP(關于自己的子串和模式串的比對問題。

hash不太會 但是考慮KMP的話 我們可以通過性質定義一種比對 自己上次的字元的比對位置和目前位置的差 恰好等于自己比對到的字元的上次位置和這次位置的內插補點。如果完全符合的話顯然和我們的性質是剛好吻合的。

考慮KMP做這件事 接下來是魔改KMP...(這題的暴力分給的很不合理 推出性質之後暴力隻有10分 正解KMP想出來了才能拿100.果然算法要套到題目上 這個角度來思考這道題更容易出解 而不是得到性質之後套到暴力之上。

建立數組 la[i] 表示 a串的i的上一個位置和目前位置的內插補點 lb[i]同理 進行比對即可。

值得注意的是 存在斷層現象 可能目前比對的長度為 w w+1上一次出現的位置與w+1的差是大于w的 此時預設為w+1上的字元為第一次出現 那麼值為w+1即可。

剩下的就沒有了。複雜度O(n)

const int MAXN=1000010;
int T,C,n,m,top;
int ans[MAXN];
int a[MAXN],b[MAXN],c[MAXN],la[MAXN],lb[MAXN];
int nex[MAXN];
inline void KMP()
{
	int j=0;top=0;
	rep(2,m,i)
	{
		while(min(j+1,lb[i])!=lb[j+1]&&j)j=nex[j];
		if(min(j+1,lb[i])==lb[j+1])++j;
		nex[i]=j;
	}
	j=0;
	rep(1,n,i)
	{
		while(min(j+1,la[i])!=lb[j+1])j=nex[j];
		if(min(j+1,la[i])==lb[j+1])++j;
		if(j==m)j=nex[j],ans[++top]=i;
	}
}
int main()
{
	freopen("1.in","r",stdin);
	//freopen("match.out","w",stdout);
	get(T);get(C);
	while(T--)
	{
		get(n);get(m);
		rep(1,C,i)c[i]=0;
		rep(1,n,i)
		{
			get(a[i]);
			la[i]=i-c[a[i]];
			c[a[i]]=i;
		}
		rep(1,C,i)c[i]=0;
		rep(1,m,i)
		{
			get(b[i]);
			lb[i]=i-c[b[i]];
			c[b[i]]=i;
		}
		KMP();
		put(top);
		rep(1,top,i)printf("%d ",ans[i]-m+1);
		puts("");
	}
	return 0;
}
           

T3:

3.27模拟賽

一道沒見過的dp優化題目。

看出來dp之後可以列狀态f[i]表示到達第i頁的最大價值。

頁數1e9當場gg 很容易發現和頁數無關 之和我們到達第i個關鍵點了沒有有關。

考慮最優政策 一定是從第K頁一路上經過若幹個關鍵點過來的。f[i]表示到達第i個點的最大價值。

容易得到轉移 \(f[i]=f[j]-\lceil\frac{(T_i-T_j)}{D}\rceil\cdot A+B_i\)

轉移是\(n^2\)的 暫時看不出來怎麼優化。

不過這個向上取整的式子我倒是可以拆成向下取整。

\(\lceil\frac{a}{b}\rceil=\frac{a-1}{b}+1\)

沒啥用。。。

不過這倒是可以進一步化簡這個式子。

\(\lfloor\frac{a-c}{b}\rfloor=\frac{a}{b}-\frac{c}{b}\)-(a%b<c%b)

到這裡可以發現 還是沒有任何用 我隻不過在玩這個式子罷了/cy 至少對寫暴力有很大的優化...

bf score:20

const ll MAXN=100010;
ll f[MAXN];
ll K,M,D,A,n,maxx=-INF;
ll a[MAXN],b[MAXN];
signed main()
{
	freopen("1.in","r",stdin);
	//freopen("reading.out","w",stdout);
	f[0]=0;get(K);get(M);get(D);get(A);get(n);
	rep(1,n,i)get(a[i]),get(b[i]),f[i]=-INF;a[0]=K;
	//if(n<=1000)
	{
		rep(1,n,i)
		{
			rep(0,i-1,j)
			{
				ll w=(a[i]-a[j]-1)/D+1;
				f[i]=max(f[i],f[j]+b[i]-w*A);
			}
		}
		rep(0,n,i)
		{
			ll w=(M-a[i]-1)/D+1;
			f[i]=f[i]-w*A;
			maxx=max(maxx,f[i]);
		}
		putl(maxx);
	}
	return 0;
}
           

考慮題目中的子任務 D<=100

觀察我們的dp式如果存在 j<k 且j和k 模D同餘 我們從dp的角度分析從j轉移過來沒啥用 直接從k轉移即可。

這樣我們得到了一個非常好些的 nD的dp.

接下來就要讓這兩個方法結合 其實光我第一個dp就可以推出正解。

考慮dp式的化簡 \(f[i]=f[j]-\lceil\frac{(T_i-T_j)}{D}\rceil\cdot A+B_i\) 這個向上取整等價于 \(\frac{T_i}{D}-\frac{T_j}{D}\)+(\(T_i\)%D>\(T_j\)%D.

絕殺化簡 對于任意一條路徑來說 對于路徑上兩個相連的點 i j 他們對答案的貢獻的\(\frac{T_i}{D}-\frac{T_j}{D}\)的連續累加 恰好等于\(\frac{M}{D}-\frac{K}{D}\).

也就是說我們隻需要管 後面那個東西 就行了 這其實是是否多了一個A的關系 采用資料結構優化一下即可。

區間查詢最值 考慮線段樹 (zkw線段樹常數會很小很小。或者标記永久化 沒必要懶标記了。這裡推薦zkw.

const ll MAXN=100010;
ll f[MAXN];
ll K,M,D,A,n,maxx=-INF,top,num,base;
ll a[MAXN],b[MAXN],c[MAXN],val[MAXN<<2];
inline void discrete()
{
	sort(c+1,c+1+num);
	rep(1,num,i)if(i==1||c[i]!=c[i-1])c[++top]=c[i];
	rep(0,n,i)a[i]=lower_bound(c+1,c+1+top,a[i])-c;
}
inline void build(ll len)
{
	while(base<=len)base=base<<1;
	rep(1,base*2,i)val[i]=-INF;
}
inline void modify(ll pos,ll x)
{
	pos+=base;val[pos]=max(val[pos],x);pos=pos>>1;
	while(pos)
	{
		val[pos]=max(val[pos<<1],val[pos<<1|1]);
		pos=pos>>1;
	}
}
inline ll ask(ll l,ll r)
{
	ll res=-INF;
	for(l=base+l-1,r=base+r+1;l^r^1;l=l>>1,r=r>>1)
	{
		if(~l&1)res=max(res,val[l^1]);//經過左兒子
		if(r&1)res=max(res,val[r^1]);//經過右兒子
	}
	return res;
}
signed main()
{
	freopen("1.in","r",stdin);
	//freopen("reading.out","w",stdout);
	get(K);get(M);get(D);get(A);get(n);
	rep(1,n,i)get(a[i]),get(b[i]),a[i]%=D,c[i]=a[i];
	num=++n;a[n]=M%D;c[n]=a[n];c[++num]=a[0]=K%D;discrete();
	base=1;build(top);
	modify(a[0],0);
	rep(1,n,i)
	{
		ll w1=ask(1,a[i]-1)-A;
		ll w2=ask(a[i],top);
		f[i]=max(w1,w2)+b[i];
		modify(a[i],f[i]);
	}
	putl(f[n]-(M/D-K/D)*A);
	return 0;
}