做題小結

LteShuai發表於2024-07-05

第一個

這道題我是真想了半天 後面還是沒想出來 哪知道是dp啊!!!
然後這個就很像揹包了 不同的是第二層是直接列舉約數裝進去 寫法上也很講究 我指的是初始化 沒有初始化!只有邊做邊初初始化 為什麼呢 因為對於所有的數而言 是取max 然後加上本身 如果一開始所有人都是 做的時候取max是啥意思! 對吧
所以我們只需要寫個On\(\sqrt{n}\)的程式

點選檢視程式碼
	sort(a + 1, a + 1 + n);
			if(a[i]%j==0)
			{
				if(j==1)add=dp[a[i]/(a[i]/j)];
				else
				add=max(max(add,dp[a[i]/(a[i]/j)]),dp[a[i]/j]);
			cout<<j<<" "<<add<<endl;
			}
		這就是為什麼dpa[i]為什麼後面++
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j * j <= a[i]; j++) {
			if (a[i] % j == 0) {
		dp[a[i]] = max(max(dp[a[i]], dp[a[i] / (a[i] / j)]), dp[a[i] / j]);
			}
		}
		dp[a[i]] ++;
		ans = min(ans, n - dp[a[i]]);
	}

但是! 時間還可以最佳化 聯想埃氏篩 對於任何一個數 可以構成倍數的化 它只對自己的倍數產生貢獻於是 我們就可以寫一個ONlogN的程式碼

給這樣一個程式碼

for (int i = 1; i <= 2e5 + 10; i++) {
    for (int j = i + i; j <= 2e5 + 10; j += i) {
        // 內層迴圈體
    }
}

外層迴圈2e5 我10下面都不打了 我當時隨便寫的
內層分析

減1是減去初始的比如我們當i=2時 0一直加到2e5 是2e5/2

i=1:2e5/1-1
i=2:2e5/2-1 
i=3:2e5/3-1
i=k:2e5/k-1

於是得出\(\sum\limits_{i=1}^n\dfrac{2e5}{i}-1\)
把2e5拖出來 裡面就是一個調和級數的表示式
Hn=\(\sum\limits_{i=1}^n\dfrac{1}{k}\)=\(ln(n)+0.577\)
那個0.577直接省略
再來說下為什麼老是明明是ln又變成log

\(lnx\)=\(loge^X\)=\(\dfrac{log2^x}{log2^e}\)
所以\(lnx\)=\(log2^X*loge^2\)
後面那個是常數直接不管了
所以回到上面
這個的時間複雜度就是ONlnN
然後直接變成Onlogn了 沒區別的其實 對於複雜度沒區別
不過埃氏篩氏是基於質數的調和級數 多一個log

回到原題
那麼程式碼就可以動手寫了 注意這裡是隨機給資料 所以我們必須要從1-n列舉 而不是1-n 列舉Ai 否則n個2的資料可以給我們卡的天上去

點選檢視程式碼
	for(int i=1;i<=2e5;i++)
	{
		dp[i]+=cnt[i];
		//cout<<dp[i]<<endl;
		for(int j=i+i;j<=2e5;j+=i)
		{
		dp[j]=max(dp[j],dp[i]);
		}
		ans=max(ans,dp[i]);	
	}

第二個

一開始想多了 後面反應過來了 把他的搶了 浮上來的對方隊頭也需要考慮對方對頭元素 然後對比損失 結果這個式子一些出來就發現搶掉對方的一定是最優的 於是就ac了
一開始想多了 。。沒想到這題這麼簡單

寫錯的

第三個非常好的一道題

題意簡述下
給你ai bi 找到一個j滿足
ai>aj bi>bj
或者ai>bj bi>ai
這題我是真做了半天 也沒做出來
我一開始用線段樹存值 發現也是錯的 存在邏輯錯誤 後面想了別的辦法 用的數軸表現(也只能輸出yes no存不了答案) 再一看資料這麼大 好了
我就知道做不出來了
看了下題解
今天早上考物理 物理卷子交了張白卷只寫了選擇上去 然後就在等提前交卷時間 在那默寫這個程式碼 因為昨晚看的題解 看完以後 有點牴觸
於是不想動手寫 看b站去了
這個程式碼早上默寫了一遍 一考完就去機房打出來了 唯一不同的是我默寫時候 我以為這個maxn是按從大到小排序的 自己試了下發現從小到大比較合適
這個程式碼非常好 簡直堪比Money Trees那道題
點贊!

點選檢視程式碼
struct node {
	int mini;
	int maxn;
	int id;
} a[range];
bool cmp(node x,node y)
{
	if(x.maxn==y.maxn)return x.mini<y.mini;
	return x.maxn<y.maxn;
}
struct Node{
	int id;
	int num;
}ans[range];
bool ccmp(Node x,Node y){return x.id<y.id;}
int num;
void init()
{
	for(int i=1;i<=n;i++)
	{
		ans[i]={0,0};
		a[i]={0,0,0};
	}
}
void solve() {
	cin >> n;
	num=0;
	for (int i = 1; i <= n; i++) {
		int x, y;
		cin >> x >> y;
		a[i].maxn = max(x, y);
		a[i].mini = min(x, y);
		a[i].id = i;
		ans[i].id=i;
		ans[i].num=-1;
	}
	sort(a+1,a+1+n,cmp);
	int mini=1e9;
	for(int i=1,t=i+1;i<=n;i=t)
	{
		while(a[i].maxn==a[t].maxn&&t<=n)t++;
		for(int j=i;j<=t-1;j++)
		{
			if(a[j].mini>mini)
			{
				ans[a[j].id].num=num;
			}
		}
		for(int j=i;j<=t-1;j++)
		{
			if(a[j].mini<mini)
			{
				mini=a[j].mini;
				num=a[j].id;
			}
		}
	}
	sort(ans+1,ans+1+n,ccmp);
	for(int i=1;i<=n;i++)
		cout<<ans[i].num<<" ";
	cout<<endl;
}

第四個

個人認為這題暴力做法比我的最佳化更難想 竟然是直接列舉前面的sum然後直接看能不能匹配 然後做的 這個只能說藝高人膽大了 我看了資料也沒想出 \(On^2\)的做法

第五個

這道題是我自己想出來的

一開始讀錯了題 以為互相都要相交 我這當時想了40多分鐘吧 真的想不下去了 這哪裡能做啊 然後我就再讀了下題 反正。。。。
服辣 於是重新思考 然後又想了下發現對右端點排序再二分查詢 是很難實現的 我於是想了下 直接把左邊整理好 右邊也整理好 直接二分
有點像之前做的那個題目

題目

這題給了我一點小啟示 於是我直接二分 然後二分的物件選舉很重要

假設此時列舉到的人左邊是st 右邊是fin

我們要找的人他的右邊比st大,但是左邊比fin小
記住這句話
於是對於樣例

4
1 2
2 3
3 5
4 5

整理成

L:1 2 3 3 4
R:4 5 5 8 8 

然後我們假設此時列舉到了3 5 也就是第三個人
我們發現可以找到L是4 R是1 看來我們可以找到3個人和他一個集合 你可能會有疑問
這裡的L R不匹配啊這是排序了的 你怎麼能保證相互對應呢

這邊你要明白 對於找比fin小的
在L陣列 我們是看1-flagL
對於找比st大的R陣列 我們的元素選取是flagR-n
我們不用管 是不是相互對應 因為Li必須小於Ri 找到了一個Li 他對於的Ri肯定是可以在flagr-n之間的 因為你Li可以找到別fin小 就一定可以相交對吧 那你的Ri肯定大於Li 那麼你肯定指標flagR肯定是在flagL之前的
因為你想想st比fin小 那fin指標肯定指後面的 因為我們fin是找左陣列的 肯定是可以走的更遠的 對於右陣列而言找一個比st大的那肯定可以在他之前找到 畢竟你想st又小又處於一個最大的R陣列裡

肯定不會出現那種相離的狀態就是flagL<flagR這種可能的 我也沒特判這種可能 直接ac 因為我知道不可能出現 最差的情況就是自己指自己也有一個貢獻

這就是我做題的思路 沒看題解上面思路 因為寫出來的程式碼都差不多

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
#define debug cout<<endl<<"----------"<<endl;
using namespace std;
const int range=3e5+10;
int n;
pair<int,int>p[range];
//無語住了 讀題讀錯了 讀成任何一條邊互相都相交
//給我想了半天都沒想出來 回頭一看題 讀錯了
//正解一會就想出來了 之前想了半天半天都沒想出來
//心裡還想這尼瑪能做的啊
//bool cmp(pair<int,int>a,pair<int,int>b)
//{
//	if(a.second==b.second)return a.first<b.second;
//	return a.second<b.second;
//}
int a[range];
int b[range];
void init()
{
	for(int i=1;i<=n+100;i++){
		a[i]=b[i]=0;
		p[i]={0,0};
	}
}
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>p[i].first>>p[i].second;
		a[i]=p[i].first;
		b[i]=p[i].second;
	}
	int ans=1e7;
	sort(a+1,a+1+n);sort(b+1,b+1+n);
	for(int i=1;i<=n;i++)
	{
	     int st=p[i].first;
		int fin=p[i].second;
	    int x=upper_bound(a+1,a+1+n,fin)-a;
		int y=lower_bound(b+1,b+1+n,st)-b;
		if(a[x]>fin)x--;
		if(x==n+1)x--;
		if(y==n+1)y--;
	  if(x<y)continue;
		int  h=x-y+1;
	//	debug
	//	cout<<st<<" "<<fin<<endl;
	//	cout<<x<<" "<<y<<endl;
		ans=min(n-h,ans);
	}
	cout<<ans<<endl;
	
	
}
signed main()
{
	ios::sync_with_stdio();
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--)
	solve();
	return 0;
	
	
} 

最後

寫完了

早點回去今天 因為今天早上一考完回宿舍了下 就來了 今天早十幾分種下班了 回宿舍洗澡 看看下雨了沒 跑步啥的

相關文章