{小社題解}

Rank

# CHAPTER 2

lemonilemon

Score: 400

1.

lemonilemon

Score: 400

1.

並沒有

# CHAPTER 2

lemonilemon

Score: 400

1.

lemonilemon

Score: 400

1.

Rank

# CHAPTER 2

lemonilemon

Score: 400

1.

🐟🔪

Score: 328

1.

Rank

# CHAPTER 2

🐑

Score: 269

3.

Score: 288

2.

{pA}

理論上他要是最簡單的(?

題目

給你一顆像是樹的東西

並給你他下面可以連幾條邊出去

問你總共會有多少邊

要加上沒有被連到的邊(空著的)

要判斷的是每個人他還能被連幾次

某個人想去連他的時候能不能連

subtask 1 10p

\(a_i=0\) 所有人都連上了代表交換器的0

算 \(n+\sum b_i\)

\(O(N)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int n, q;cin>>n>>q;
	int ans=0;
	for(int i=0; i<q; i++){
		string r;cin>>r;
		int a, b, c;cin>>a>>b>>c;
		ans+=c+1;
	}cout<<ans;
}

subtask 2 13p

\(b_i=N\) 所有人都可以被 \(N\) 個人連上

不用擔心會有人連到不該連的 一定夠

對於每次連接 若接上交換器則答案加\(b_i+1\) 否則加\(b_i\)

\(O(N)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int n, q;cin>>n>>q;
	int ans=0;
	for(int i=0; i<q; i++){
		string r;cin>>r;
		int a, b, c;cin>>a>>b>>c;
		ans+=c;
		if(b==0) ans++;
	}cout<<ans;
}

subtask 3 27p

我寫題解的時候甚至沒印象我出過這個

但你想的到\(O(N^2)\) 的解法就可喇(?

AC

我覺得解蠻直觀的ㄅ

開一個陣列存每個人還能分給幾個人

每次有人連他就--

連成功之後按照給的數字更改連的人的剩餘

如果要去連上一個剩零配額的人就報錯誤訊息

這題N蠻大的可以開全域變數或vector避免SIG

\(O(N)\)

# PRESENTING CODE

AC CODE

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second
vector<int> canc(5000007, 0);
signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int n, q;cin>>n>>q;
	int ans=0;
	canc[0]=INT32_MAX;
	for(int i=0; i<q; i++) {
		string name;cin>>name;
		int a, b, c;cin>>a>>b>>c;
		if(canc[b]){
			canc[a]=c;
			ans+=c;
			if(b==0) ans++;
			canc[b]--;
		}else{
			cout<<name<<" wanna steal internet\n";
		}
	}
	cout<<ans;
}

{pB}

墜河了

題目

給你一串陣列

每次問你一段區間的XOR SUM

btw題敘我用chatGPT寫的 還好大家都看懂了吧

subtask 1 40p

\(N, Q \le 1000\)

報搜就好

對每次query跑一個for xor在一起就是了

聽說這個subtask變成簽到了

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int n, q;cin>>n>>q;
	vector<int> li(n);
	for(int i=0; i<n; i++) cin>>li[i];
	for(int i=0; i<q; i++){
		int a, b;cin>>a>>b;
		int ans=0;
		for(int j=a; j<=b; j++){
			ans^=li[j];
		}cout<<ans<<'\n';
	}
}

AC

\(N, Q \le 100000\)

本來沒有很想講

但賽中加了個提示

a^b^b=a

意思是一個東西被xor到兩次的話他會消失

所以我們就可以用前綴和的方法去寫ㄌ

對於每一項 紀錄從第一項xor過來的值

對於一個要查詢的\([l,\ r]\)區間

就算prexor[r]^prexor[l-1]

需要的區間的每一個都被算到一次

不需要的都被算到零次或二次

# PRESENTING CODE

AC CODE

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int n, q;cin>>n>>q;
	vector<int> pre(n+1);
	for(int i=1; i<=n; i++){
		int a;cin>>a;
		pre[i]=pre[i-1]^a;
	}
	for(int i=0; i<q; i++){
		int a, b;cin>>a>>b;
		cout<<(pre[a]^pre[b+1])<<'\n';
	}
}

{pC}

對不起我是小丑

題目

如果你知道甚麼是拓撲排序的話(請參閱圖論[1])

你會發現這是用pq拓排加上找LCS的裸題

subtask 1 80p

pq拓撲排序複雜度為 \(O(N\ logN)\)

LCS的複雜度為\(O(N^2)\)

你會發現會炸 壓LCS的方法因為真的有點難靠自己通靈

所以這個subtask就先給80p了

記得要確切決定要走到哪一個點的時候再紀錄拓排順序

# PRESENTING CODE

Code Transitions

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int n, m;cin>>n>>m;
	vector<int> ind(n, 0);
	vector<vector<int>> adj(n);
	for(int i=0; i<m; i++){
		int a, b;cin>>a>>b;
		adj[a].pb(b);
		ind[b]++;
	}
	priority_queue<int, vector<int>, greater<int>> q;
	vector<int> seq;
	for(int i=0;i<n; i++){
		if(ind[i]==0) {q.push(i);}
	}
	vector<int> taken(n, 0);
	while(!q.empty()){
		int tmp=q.top();q.pop();
		//cout<<tmp<<' ';
		if(!taken[tmp]){ seq.pb(tmp);taken[tmp]=1;}
		for(int x:adj[tmp]){
			ind[x]--;
			if(ind[x]==0){
				//seq.pb(x);
				q.push(x);
				//taken[x]=1;
				//cout<<x<<' ';
			}
		}
	}
	int n2, m2;cin>>n2>>m2;
	vector<int> ind2(n2, 0);
	vector<vector<int>> adj2(n2);
	for(int i=0; i<m2; i++){
		int a, b;cin>>a>>b;
		adj2[a].pb(b);
		ind2[b]++;
	}
	priority_queue<int, vector<int>, greater<int>> q2;
	vector<int> seq2;
	for(int i=0;i<n; i++){
		if(ind2[i]==0) {q2.push(i);}
	}vector<int> taken2(n2, 0);
	while(!q2.empty()){
		int tmp=q2.top();q2.pop();
		if(!taken2[tmp]) {seq2.pb(tmp);taken2[tmp]=1;}
		for(int x:adj2[tmp]){
			ind2[x]--;
			if(ind2[x]==0){
				//seq2.pb(x);
				q2.push(x);
				//taken2[x]=1;
			}
		}
	}
	vector<vector<int>> dp(n+1, vector<int>(n+1, 0));
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			if(seq[i-1]==seq2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
			else{
				dp[i][j]=max(dp[i-1][j], dp[i][j-1]);
			}
		}
	}
	cout<<dp[n][n];
}

AC

因為你會發現

兩個拓撲排序順序的結果都會是

一條1~N的排列

這樣可以透過將LCS透過歸約成LIS將複雜度壓到\(O(N\ logN)\)

為什麼呢 大家可以自己回家想想看

絕對不是我懶

# PRESENTING CODE

AC CODE

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int n, m;cin>>n>>m;
	vector<int> ind(n, 0);
	vector<vector<int>> adj(n);
	for(int i=0; i<m; i++){
		int a, b;cin>>a>>b;
		adj[a].pb(b);
		ind[b]++;
	}
	priority_queue<int, vector<int>, greater<int>> q;
	vector<int> seq;
	for(int i=0;i<n; i++){
		if(ind[i]==0) {q.push(i);}
	}
	vector<int> taken(n, 0);
	while(!q.empty()){
		int tmp=q.top();q.pop();
		if(!taken[tmp]){ seq.pb(tmp);taken[tmp]=1;}
		for(int x:adj[tmp]){
			ind[x]--;
			if(ind[x]==0){
				q.push(x);
			}
		}
	}
	int n2, m2;cin>>n2>>m2;
	vector<int> ind2(n2, 0);
	vector<vector<int>> adj2(n2);
	for(int i=0; i<m2; i++){
		int a, b;cin>>a>>b;
		adj2[a].pb(b);
		ind2[b]++;
	}
	priority_queue<int, vector<int>, greater<int>> q2;
	vector<int> seq2;
	for(int i=0;i<n; i++){
		if(ind2[i]==0) {q2.push(i);}
	}vector<int> taken2(n2, 0);
	while(!q2.empty()){
		int tmp=q2.top();q2.pop();
		if(!taken2[tmp]) {seq2.pb(tmp);taken2[tmp]=1;}
		for(int x:adj2[tmp]){
			ind2[x]--;
			if(ind2[x]==0){
				q2.push(x);
			}
		}
	}


	vector<int> tmp(n);
	for(int i=0; i<n; i++){
		tmp[seq2[i]]=i;
	}
	for(int i=0; i<n; i++){
		seq[i]=tmp[seq[i]];
	}
	vector<int> LIS;
	for(int i = 0; i < n; ++i) {
        int buf=seq[i];
        if(LIS.empty() || buf > LIS.back()) { 
			LIS.push_back(buf);
        }
        else {
            auto p = lower_bound(LIS.begin(), LIS.end(), buf);
			*p = buf;
		}
    }cout<<LIS.size();	
}

{pD}

我不會DP

題目

分組背包我是出完才知道有分組背包這東西的

如果妳會背包問題你就應該會的東西

多選一或0

subtask 1 1p

暴力計算當前得分

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
    ios_base::sync_with_stdio(false),cout.tie(0),cin.tie(0);
    int n,T,cnt=0;
    cin>>n>>T;
    vector<vector<int>> score(n);
    vector<vector<bool>> solved(n);
    for(int i=0,a;i<n;i++){
        cin>>a;
        solved[i].resize(a,false);
        for(int j=0,v;j<a;j++) cin>>v,score[i].push_back(v);
    }
    for(int i=0,c;i<n;i++){
        cin>>c;
        for(int j=0,b;j<c;j++) cin>>b,solved[i][b]=true,cnt+=score[i][b];
    }
    cout<<cnt<<"\n";
}

然後你就會發現你也過了

\(subtask2\)和\(subtask4\)

subtask 1,2,4 5p

subtask 3

計算完初始得分後,判斷當前時間是否夠在解一次

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
    ios_base::sync_with_stdio(false),cout.tie(0),cin.tie(0);
    int n,T,cnt=0;
    cin>>n>>T;
    vector<vector<int>> score(n);
    vector<vector<bool>> solved(n);
    for(int i=0,a;i<n;i++){
        cin>>a;
        solved[i].resize(a,false);
        for(int j=0,v;j<a;j++) cin>>v,score[i].push_back(v);
    }
    for(int i=0,c;i<n;i++){
        cin>>c;
        for(int j=0,b;j<c;j++) cin>>b,solved[i][b]=true,cnt+=score[i][b];
    }
    int k,s,t;
    cin>>k;
    cin>>s>>t;
    if(t<=T){
        for(int u;s--;) cin>>u,cnt+=solved[0][u]?0:score[0][u];
    }
    cout<<cnt<<"\n";
}

subtask 5

忘記計算目前得分

subtask 6

沒有subtask

subtask 7

直接做背包

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
    ios_base::sync_with_stdio(false),cout.tie(0),cin.tie(0);
    int n,T,cnt=0;
    cin>>n>>T;
    vector<vector<int>> score(n);
    vector<vector<bool>> solved(n);
    for(int i=0,a;i<n;i++){
        cin>>a;
        solved[i].resize(a,false);
        for(int j=0,v;j<a;j++) cin>>v,score[i].push_back(v);
    }
    for(int i=0,c;i<n;i++){
        cin>>c;
        for(int j=0,b;j<c;j++) cin>>b,solved[i][b]=true,cnt+=score[i][b];
    }
	vector<int> dp(T+1,cnt);
	for(int i=0,k,value,s,t;i<n;i++){
		cin>>k>>s>>t;
		value=0;
		for(int u;s--;) cin>>u,value+=solved[i][u]?0:score[i][u];
		for(int j=T;j>=t;j--) dp[j]=max(dp[j],dp[j-t]+value);
	}
    cout<<dp[T]<<"\n";
}

subtask 8

沒有比較好做

AC

  • 先預處理好已經得到的分數
  • 先預處理好每個解法的得分,記得排掉已經通過的
  • 利用\(dp_t\)儲存花費\(t\)的時間的最大得分
  • 對於每個題目做:
    • 對於特定時間:
    • 利用dp計算選擇該解法的最大得分
    • \(dp_t=max(dp_t,dp_{t-timecost_i}+value_i)\)
    • \(i\)為所有解法
    • 記得排掉超出時間的
  • 每個題目相互獨立可以直接做
# PRESENTING CODE

AC CODE

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
    ios_base::sync_with_stdio(false),cout.tie(0),cin.tie(0);
    int n,T,cnt=0;
    cin>>n>>T;
    vector<vector<int>> score(n);
    vector<vector<bool>> solved(n);
    for(int i=0,a;i<n;i++){
        cin>>a;
        solved[i].resize(a,false);
        for(int j=0,v;j<a;j++) cin>>v,score[i].push_back(v);
    }
    for(int i=0,c;i<n;i++){
        cin>>c;
        for(int j=0,b;j<c;j++) cin>>b,solved[i][b]=true,cnt+=score[i][b];
    }
    vector<int> dp(T+1,cnt);
    vector<int> cost, value;
    for(int i=0,k;i<n;i++){
        cin>>k;
        cost.resize(k),value.resize(k);
        for(int y=0,t;y<k;y++){
            cin>>t>>cost[y];
            value[y]=0;
            for(int z=0,u;z<t;z++){
                cin>>u;
                if(!solved[i][u]) value[y]+=score[i][u];
            }
        }
        for(int j=T,best;j>0;j--){
            best=dp[j];
            for(int y=0;y<k;y++){
                if(j-cost[y]<0) continue;
                best=max(best,dp[j-cost[y]]+value[y]);
            }
            dp[j]=best;
        }
    }
    cout<<dp[T]<<'\n';
}

{pE}

你是電神🉐

題目

有\(n\)個人是你的對手,他們編號大的會贏編號小的

會和這些人對打,打每個人需要\(a_i\)的電力,你有\(m\)的電力,你最高是第幾名

subtask 1 1p

你沒體力,打人又要耗體力,輸光

你是最後一名

輸出\(n+1\)

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    cout<<n+1;
}

subtask 2,3

沒有比較好做

AC

對於那\(n\)個人當中,想一下他們贏的場次

第\(i\)個人贏了\(i\)場(0-base)

將\(a_i\)由小到大排序,從小的開始打

計算你能贏多少場

但還沒結束

你需要考慮其他人會不會多贏

你贏的人有沒有在剛剛打贏的人裡面

# PRESENTING CODE

AC CODE

#include<bits/stdc++.h>
using namespace std;
int main(){
	ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int n,m,cnt;
    cin>>n>>m;
	vector<pair<int,int>> a(n);
    cnt=0;
    for(pair<int,int> &i:a) cin>>i.first;
    for(int i=0;i<n;i++) a[i].second=i;
    sort(a.begin(),a.end());
    for(int i=0;i<n;i++){
        if(m>=a[i].first){
            m-=a[i].first;
            cnt++;
        }
        else if(i){
            m+=a[i-1].first;
            for(i=0;i<n&&a[i].first<=m;i++){
                if(a[i].second==cnt){
                    cnt++;
                    break;
                }
            }
            break;
        }
        else break;
    }
    cout<<n+1-cnt<<'\n';
}

{pF}

你聽說過 費馬小定理

沒聽過的話請去問學測倒數15天的AaW

因為這題他想的 得

題目

給你一個雞兔同籠的問題

喔不對是佑佑807配對問題

算出答案之後要乘上一個奇怪的東西

大概是 \((k^n)\%m\)

subtask 1 69p

$$ t=1,  n < 10000 $$

應該不用我教你怎麼算雞兔同籠ㄅ

反正算完之後佑佑跟807的數量取min之後

慢慢乘就會得到答案了

\(O(\sum n)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int t;cin>>t;
	while(t--){
		int a, b, n, m, k;
		cin>>a>>b>>n>>m>>k;
		int tmp=(b*807-a)/805;
		int tpp=b-tmp;
		int ans=min(tmp, tpp);
		int ti=k;
		for(int i=1; i<n; i++){
			ti=(ti*k)%m;
		}cout<<ans*ti;
	}
}

subtask 2 18p

$$ n < 10^{15}  $$

還記得快速冪ㄅ

\(O(\sum log_2(n))\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define pb push_back
#define all(x) x.begin(),x.end()
#define ff first
#define ss second

int fast(int n,int k, int m){
	if(k==0) return 1;
	if(k==1) return n%m;
	int dx=fast(n, k/2, m)%m;
	if(k%2) return (((dx*dx)%m)*n)%m;
	else return (dx*dx)%m;
}

signed main(){
	ios_base::sync_with_stdio(false);cin.tie(0);
	int t;cin>>t;
	while(t--){
		int a, b, n, m, k;
		cin>>a>>b>>n>>m>>k;
		int tmp=(b*807-a)/805;
		int tpp=b-tmp;
		int ans=min(tmp, tpp);
		int ti=fast(k, n, m);
		cout<<ans*ti<<'\n';
	}
}

AC

$$ n < 10^{87} $$

所以甚麼是 費馬小定理  ?

\(a^p \equiv a \ (mod \ p)\)

若a非p的倍數

\(a^{p-1} \equiv 1 \ (mod \ p)\)

所以哪裡要用到費馬小定理?

AC

我們來解讀一下

n 除以\([m - (1到m之間所有和m的最大公因數大於1的數的數量)] \)

的商為非負整數且m為質數

\([m - (1到m之間所有和m的最大公因數大於1的數的數量)] \)

(1到m之間所有和m的最大公因數大於1的數的數量)

甚麼意思?

[1, m-1]中和m不互質的數字的個數

[1, m-1]中和m互質的數字的個數

不是阿m就質數當然跟每個人互質 所以是m-1

n是m-1的倍數

AC

n是m-1的倍數

所以呢

\(k^n\%m\)是甚麼?

\(k^{d*(m-1)} \ \%\ m\)

\( {k^{m-1}}^d  \ \%\ m\)

\(1^d \ \% \ m\)

1

對於每筆詢問

請直接輸出佑佑807配對結果的對數

如果你說

不是阿幹誰知道費馬小定理是甚麼

請認真看題敘

其中一段為

AaW 跑去問佑佑:「教我費馬大定理」
「I consider 小的 to be better than 大的。」佑佑說。

which means, 費馬小定理

我們沒有限制網路搜索喔

 

下學期可能第一堂課會有你們學長的學長的學長來教你們數學

就是那個每次小社賽都在炸魚的lemonilemon

聽起來好老歐

我本來在想會不會有人無聊亂試直接算完丟上去讓他跑

結果大家都太乖ㄌ

這種沒有penalty的比賽就是要亂丟阿

狂丟

丟到主機炸掉

 

不對 我要被隔壁的老師罵了

大家有發現我們大小設賽怎麼取題目名字的嗎

Code

By ckefgisc28th

Code

  • 176