在前面的話
其實這次比賽總體來說比去年簡單一些,可惜我考場的時候沒有 debug ,考試的時候整個人的狀態也不好,所以考試完全發揮失常
T1
題目鏈接
問題解決
想要處理這道題需要引用一個結論:
- 如果一個飛機在 \(i\) 個廊橋時占用廊橋,那么在 \(i+1\) 個廊橋時也必然占用廊橋
我們可以這樣證明理解這個結論:
因為先到的飛機是先占用廊橋的,若這個飛機在 \(i\) 的時候可以占用廊橋,也就是說這個飛機到達的時間和前一個飛離的時間是最接近的,所以多了一個廊橋時,這些飛機的相對關系不會發生變化,所以這個飛機必然能占用廊橋。
也就是說,若離這個飛機最近的飛機在 \(i+1\) 時刻占用廊橋,那么這個飛機在 \(i+1\) 時刻也可以占用廊橋
顯然,第一個到達的飛機是可以占用廊橋的 (如果廊橋數大於一)
有點像數學歸納法的感覺,總之這個結論還是很好接受的
知道了這個結論后就比較簡單了,我們可以模擬廊橋增加的過程
國內和國外分開處理
通過圖片可以很清楚的看出,第\(i\)個廊橋能接納的飛機,我們只要找到離一個區間右邊界最近的左端點就好了
其實這里的處理方法很多,\(O(n),O(nlogn)\) 的都可以過,因為我比較菜,所以寫了 \(O(nlogn)\) 的寫法
將區間左端點放入集合中,然后按右端點查找,在集合中刪去就可以了
將國內和國外分別處理完后,枚舉一次就好了
代碼實現
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int N,M1,M2,numa[maxn],numb[maxn],ans,q[maxn<<1];
struct air{
int L,R;
bool operator <(const air B)const {return this->L<B.L;}
bool operator ==(const air B)const {return this->L==B.L&&R==B.R;}
}a[maxn],b[maxn],INF,iINF;
set<air> P;
typedef set<air>::iterator it;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
int main(){
N=read();M1=read();M2=read();
for(int i=1;i<=M1;i++) a[i].L=read(),a[i].R=read(),q[++q[0]]=a[i].L,q[++q[0]]=a[i].R;
for(int i=1;i<=M2;i++) b[i].L=read(),b[i].R=read(),q[++q[0]]=b[i].L,q[++q[0]]=b[i].R;
sort(q+1,q+1+q[0]);sort(a+1,a+1+M1);sort(b+1,b+1+M2);
for(int i=1;i<=M1;i++) a[i].L=lower_bound(q+1,q+1+q[0],a[i].L)-q,a[i].R=lower_bound(q+1,q+1+q[0],a[i].R)-q;
for(int i=1;i<=M2;i++) b[i].L=lower_bound(q+1,q+1+q[0],b[i].L)-q,b[i].R=lower_bound(q+1,q+1+q[0],b[i].R)-q;
for(int i=1;i<=M1;i++) P.insert(a[i]); INF.L=0x3f3f3f3f,INF.R=0x3f3f3f3f; P.insert(INF); iINF.L=0; iINF.R=0;
int i=1;for(int cnt=0;cnt<M1&&!P.empty();i++){
it pos=P.begin();int lst=cnt;
while(1){
air now=*pos;++cnt;now.L=now.R,now.R=0x3f3f3f3f;
P.erase(pos);pos=P.upper_bound(now);
if((*pos)==INF)break;
}
numa[i]=cnt-lst+numa[i-1];
}
for(;i<=N;i++)numa[i]=numa[i-1];
P.clear();
for(int i=1;i<=M2;i++) P.insert(b[i]); P.insert(INF);
i=1;for(int cnt=0;cnt<M2&&!P.empty();i++){
it pos=P.begin();int lst=cnt;
while(1){
air now=*pos;++cnt;now.L=now.R,now.R=0x3f3f3f3f;
P.erase(pos);pos=P.upper_bound(now);
air p=*pos;
if((*pos)==INF)break;
}
numb[i]=cnt-lst+numb[i-1];
}
for(;i<=N;i++)numb[i]=numb[i-1];
for(int i=0;i<=N;i++)ans=max(ans,numa[i]+numb[N-i]);
printf("%d\n",ans);
return 0;
}
T2
題目鏈接
這個題一看就是區間 DP,應該沒有什么問題吧~,不知道也沒有其他做法,反正我只能看出是區間 DP
就存在如何定義的問題
很顯然的,我們可以定義 \(F[i][j]\) 表示從 \(i\) 到 \(j\) 的合法方案數
考慮轉移,我們發現無非就是幾種情況,按照題目的意思寫即可
實現我們為了判定一個串是否是 * 的一段,也就是 \(S\) 需要構造一個 \(sum[i]\) 表示 \(i\) 之前的 * 和 ? 的個數,查找一段的時候相減即可
-
()/(S) 這兩種本質上是相同的,只要判斷 \(i\) 到 \(j\) 內部是否都是 * ,若是的則\(F[i][j]++\)
-
(AS) \(F[i][j]+=F[i+1][k]\),\(k\) 是內部區間的右端點,要保證從 \(k\)到\(j\) 都是 * 方可轉移
-
(SA) 和 (AS) 相同 \(F[i][j]+=F[k][j-1]\) ,\(k\) 是內部區間的左端點,要保證從 \(i\) 到 \(k\) 都是 * 方可轉移
-
(A) 不解釋了 \(F[i][j]+=F[i+1][j-1]\) 這個不理解的回爐重造吧
-
AB/ASB 這個時最難的,我們需要枚舉內部兩個區間的斷點,設\(k1\) 表示內部左區間的右端點,\(k2\) 表示右邊區間的右端點,顯然需要從 \(k1\) 到 \(k2\) 都是 * 方可轉移,\(F[i][j]+=F[i][k1]\times F[k2][j]\)
這里我們發現自己已經寫了個 \(O(n^4)\)的DP了,其實還有一些小問題,對於 AB/ASB這種情況會算重
例如 ()()() 這組數據計算 AB/ASB 時被算了兩次,一次()+()(),一次 ()()+(),仔細想想是不是
所以我們需要從問題的源頭入手,是不是定義除了問題,考慮再加入一個定義:\(G[i][j]\) 表示左端點和右端點必須是一對配對的括號的方案數,例如:(()())是合法的,(())()是不合法的,這個定義很重要
我們發現()()+()這次計算是沒有必要的,所以要把它除去,需要修改 5 的轉移方程,變成 \(F[i][j]+=G[i][k1]\times F[k2][j]\),就可以將重復去掉
考慮 \(G[i][j]\) 如何得到,發現對於 1,2,3,4 的情況 \(F\) 和 \(G\) 的轉移完全相同,就是 5 的情況是不可能得到 \(G\) 的
好了現在我們的代碼已經可以跑 \(n=100\) 左右的數據了,考慮如何優化
瓶頸卡在 5 的轉移:\(F[i][j]+=G[i][k1]\times F[k2][j]\),每個\(k1\) 都要乘上 \(\sum_{\text{合法的k2}} F[k2][j]\) ,顯然 \(k2\) 是連續一段的,很容易想到構造一個前綴和,每次只需要乘一次就可以了
此時時間復雜度已經優化到了 \(O(n^3)\) 可以過此題了
代碼實現
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=505;
const LL TT=1e9+7;
int N,K,nxt[maxn];
LL sum[maxn],F[maxn][maxn],G[maxn][maxn],S[maxn];
char s[maxn];
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline LL min(LL a,LL b){return a<b?a:b;}
inline LL max(LL a,LL b){return a>b?a:b;}
int main(){
N=read();K=read();int q=0;
scanf("%s",s+1);
for(int i=1;i<=N;i++) sum[i]=sum[i-1]+(s[i]=='*'||s[i]=='?');
nxt[N]=N;for(int i=N-1;i;i--) nxt[i]=(s[i]=='*'||s[i]=='?')?nxt[i+1]:i;
for(int i,j,k,k1,k2,L=1;L<N;L++)
for(i=1;i+L<=N;i++) if(s[i]!='*'&&s[i]!=')'){
j=i+L;if(s[j]=='('||s[j]=='*')continue;
(j-i-1==sum[j-1]-sum[i]&&j-i-1<=K)&&((G[i][j]=(F[i][j]+1)%TT)|(F[i][j]=(F[i][j]+1)%TT),0);
if(j-i-1<2)continue;
F[i][j]=(F[i][j]+F[i+1][j-1])%TT; G[i][j]=(G[i][j]+F[i+1][j-1])%TT;
for(k=2;k-1<=K&&i+k<j-1;k++) sum[i+k-1]-sum[i]==k-1&&((F[i][j]=(F[i][j]+F[i+k][j-1])%TT)|(G[i][j]=(G[i][j]+F[i+k][j-1])%TT),0);
for(k=2;k-1<=K&&i+1<j-k;k++) sum[j-1]-sum[j-k]==k-1&&((F[i][j]=(F[i][j]+F[i+1][j-k])%TT)|(G[i][j]=(G[i][j]+F[i+1][j-k])%TT),0);
S[i+1]=0;for(k=i+2;k<j;k++) S[k]=(S[k-1]+F[k][j])%TT;
for(k1=i+1;k1<j-1;k1++) F[i][j]=(F[i][j]+G[i][k1]*(S[min(j-1,min(nxt[k1+1],k1+K+1))]-S[k1]+TT)%TT)%TT;
// for(k1=i+1;k1<j-1;k1++)
// for(k2=k1+1;k2<j&&k2-k1-1<=K;k2++){
// (sum[k2-1]-sum[k1]==k2-k1-1)&&(F[i][j]=(F[i][j]+G[i][k1]*F[k2][j]%TT)%TT,0);
// }
}
printf("%lld\n",F[1][N]);
return 0;
}
T3
題目描述
這道題的做法也很多,我主要講我看到最簡單的一種,來自於zzj
我們先觀察樣例,很容易的能得出一個結論:對於一個串來說,外面有\(n\)個是在回文串前面的,里面的\(n\)個是在回文串后面的
在樣例中,藍色的表示前面的回文,綠色的表示后面的回文,藍色在外面,綠色在里面,和我們的結論一樣,其實結論也挺顯然的
所以我們要找出一個在外部和在內部的點模擬就好了
顯然的,第一個點肯定是最左邊和最右邊的任意一個,我們假設最左邊的點為第一個被取走的點,它肯定在藍色的部分,這個數對應的另外一個位置肯定在綠色的部分,於是,我們就可以模擬了
思考,如何模擬匹配的過程
先設四個指針 \(L1,L2,R1,R2\) 表示左邊藍色的右端點,綠色的左端點,綠色的右端點,右邊藍色的右端點,此時的綠色和藍色的交接位置是不確定的,我們就要模擬出來
觀察串是怎么匹配的,在前面的回文上一個位置上為 \(L\) ,那么從左邊取,則,\(L1++\),若從右邊,則 \(R2--\) 。在后邊的回文上的一個位置上為 \(L\) 則\(L2++\) ,若從右邊,則 \(R1--\)
考慮到前面和后面是一樣的,我們考慮一起處理,也就是第 \(i\) 位和第 \(N\times 2-i+1\) 位一起處理,我們采用一種逆向思維還原匹配過程,我們就是要找藍色和綠色交界的位置
-
若 \(s[L1]=s[L2]\) 則前面的串從左邊取,后面的串也從左邊取,\(L1++,L2--\)
(此時為什么不是 \(L2++\),因為我們反向模擬匹配的過程,可以看出還原后綠色向右邊移動了一格,也就代表着這個是在后面的回文中出現的,這個思想很重要),這個時候 -
若 \(s[L1]=s[R1]\) 則前面的串從左邊取,后面的串從右邊取,\(L1++,R1++\)
-
若 \(s[L2]=s[R2]\) 則前面的串從右邊取,后面的串從左邊取,\(L2--,R2--\)
-
若 \(s[R1]=s[R2]\) 則前面的串從右邊取,后面的串從右邊取,\(R1++,R2--\)
-
若四種方式都不可以匹配,則無解,也挺顯然的
我們在匹配的時候已經處理好取的順序了,輸出即可,當然要注意一個小細節,也就是取完后 \(L1\) 會大於 \(L2\) 或者 \(R1\) 大於 \(R2\) ,顯然不可能取的。
題目中還需要字典序最小,我們只需要按照 1,2,3,4,的順序判斷能不能取,肯定是最小的,因為 \({L,L}<L,R<R,L<R,R\)
現在已經解決了一遍的問題了,若第一個匹配的數字為右邊的,那么同理即可,這里留給大家自己思考
代碼實現
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e6+5;
int N,L1,L2,R1,R2,ans[maxn],a[maxn],ok,T;
char ch[3]="LR";
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
int main(){
fin>>T;
while(T--){
fin>>N;N<<=1;ok=0;
for(int i=1;i<=N;i++) fin>>a[i];
for(int i=2;i<=N;i++) if(a[i]==a[1]){L2=i-1;R1=i+1;break;}; L1=2;R2=N;
ans[1]=0;ans[N]=0;
for(int i=2;i<=(N>>1);i++) {
if(L1<L2&&a[L1]==a[L2]) L1++,L2--,ans[i]=0,ans[N-i+1]=0;
else if(L1<=L2&&R1<=R2&&a[L1]==a[R1]) L1++,R1++,ans[i]=0,ans[N-i+1]=1;
else if(L1<=L2&&R1<=R2&&a[L2]==a[R2]) L2--,R2--,ans[i]=1,ans[N-i+1]=0;
else if(R1<R2&&a[R1]==a[R2]) R1++,R2--,ans[i]=1,ans[N-i+1]=1;
else {ok=1;break;}
}
if(ok==0) {for(int i=1;i<=N;i++)putchar(ch[ans[i]]);putchar('\n');continue;}
ok=0;
for(int i=1;i<N;i++) if(a[i]==a[N]){L2=i-1;R1=i+1;break;}; L1=1;R2=N-1;
ans[1]=1;ans[N]=0;
for(int i=2;i<=(N>>1);i++) {
if(L1<L2&&a[L1]==a[L2]) L1++,L2--,ans[i]=0,ans[N-i+1]=0;
else if(L1<=L2&&R1<=R2&&a[L1]==a[R1]) L1++,R1++,ans[i]=0,ans[N-i+1]=1;
else if(L1<=L2&&R1<=R2&&a[L2]==a[R2]) L2--,R2--,ans[i]=1,ans[N-i+1]=0;
else if(R1<R2&&a[R1]==a[R2]) R1++,R2--,ans[i]=1,ans[N-i+1]=1;
else {ok=1;break;}
}
if(ok==0) {for(int i=1;i<=N;i++)putchar(ch[ans[i]]);putchar('\n');continue;}
putchar('-');putchar('1');putchar('\n');
}
return 0;
}
T4
題目鏈接
此題本人只會寫 \(k=2\) 時的點,此想法來自於 ix35,%%%,我把它描述的清楚一點
非常顯然,當 \(k=2\) 的時候,這是一個最小割問題,不知道的建議右轉百度
而這又是一個對偶圖問題,關於對偶圖的最大流問題可以轉化成最短路,這是一種常用套路,推薦一道類似的題目 :狼抓兔子
把一個空看成一個節點,把邊看成空和空之間的邊,重新建圖
在轉化后,從綠色節點到藍色節點的距離就是這個圖中的最小割
可以怎樣理解這個問題,若有一條路徑綠色節點和藍色節點聯通,那么這條路徑可以將這個圖分成兩部分,黑白兩點分別在兩個部分里面
那么 \(k=2\) 的點就解決了
當 \(k!=2\) 的時候,我們也可以采用同樣的方法,順時針看,從黑到白的看成藍點,從白到黑的看成綠點
我們可以把一排的綠點看成一個超級綠點,藍點同理,因為一排相同顏色點是等價的,所以綠藍點時交錯出現的,設 \(cnt\) 為塊的個數
一條線可以划分出兩個區間,將兩個區域分開,我們為了使得總的代價最小,肯定希望一條線能划開多個區間或者不要交錯,因為交錯了的代價肯定更劣
可以感性用上的這張圖來理解,但是實際上和這個題目還是有點差異的
我們可以用 \(dij\) 或者 \(spfa\)(它已經死了) 來求出 \(g[i][j]\) 表示從第 \(i\) 個塊和第 \(j\) 個塊分離的最小代價
考慮一個塊,必然要和其他不同顏色的塊分開,也就是說要單獨在一個區域內
觀察發現 \(k\) 非常小,就可以使用 \(DP\) 來枚舉每種情況,定義 \(F[i][j]\) 表示從第 \(i\) 個串到第 \(j\) 個串能完全匹配的最小代價,初始肯定是 \(F[i][i+1]=g[i][i+1]\)
分為兩種情況
-
將 \(i,j\) 分成兩部分分開匹配,也就是 \(F[i][j]=F[i][k]+F[k+1][j]\)
-
\(i,j\) 兩個點匹配,\(F[i][j]=F[i+1][j-1]+g[i][j]\)
最后的答案也就是 \(F[1][cnt]\)
單這里還有一個小問題,就是這個圖是一個環,我們要拆成鏈處理,轉移方程都不變,就是處理答案的時候需要掃一遍 \(F[i][i+cnt-1]\) 的最大值即可
代碼里面細節巨多,我在實現的時候沒有每次去建圖,而是用了 \(vector\) 來刪掉重復的邊,沒有設超級綠點和超級藍點,用了修改邊后的第一個點來作為基准點,因為基准點到這一塊所有點的距離都是 \(0\),所以直接拿基准點作為超級綠點/藍點就可以了
其他細節看代碼吧,時間復雜度 \(O(T\times (k^3+knmlognm))\)
代碼實現
#pragma GCC optimize(2)
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int N,M,T,K;
int mp[2][505][505],line[2005][3],node[2005][2],id[2005],G[2005][2005],dis[260105],vis[260105],cnt,F[2005][2005],ans;
struct edge{
int x,w;
bool operator <(const edge B)const {return w<B.w;}
}hep[260105];
int hep_len;
vector<edge> e[260105];
inline void add_e(int x,int y,int z){
e[x].push_back((edge){y,z});
}
inline void add_edge(int x,int y,int z){
e[x].push_back((edge){y,z});e[y].push_back((edge){x,z});
}
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
inline int calc(int x,int y){return (x-1)*(M-1)+y;}
bool cmp(int x,int y){return line[x][0]<line[y][0];}
int Nxt(int x){
return x==(N+M<<1)?1:x+1;
}
void update_edge(int x,int y,int z){
int flg=0;
vector<edge>::iterator it=e[x].begin();
for(;it!=e[x].end();++it){
if(it->x==y){
if(it->w==z){flg=1;break;}
else {e[x].erase(it);break;}
}
}
if(!flg)add_e(x,y,z);
it=e[y].begin();flg=0;
for(;it!=e[y].end();++it){
if(it->x==x){
if(it->w==z){flg=1;break;}
else {e[y].erase(it);break;}
}
}
if(!flg)add_e(y,x,z);
return ;
}
void put(edge x){
hep[++hep_len]=x;int son=hep_len;
while(son>1&&hep[son]<hep[son>>1]) swap(hep[son],hep[son>>1]),son>>=1;
return ;
}
edge get(){
edge ret=hep[1];int son,fa=1;hep[1]=hep[hep_len--];
while((fa<<1)<=hep_len){
if((fa<<1)>hep_len||hep[fa<<1]<hep[fa<<1|1]) son=fa<<1;else son=fa<<1|1;
if(hep[son]<hep[fa]) swap(hep[son],hep[fa]),fa=son;else break;
}
return ret;
}
void dij(int s){
memset(dis,63,sizeof dis);
memset(vis,0,sizeof vis);
dis[s]=0;edge now;now.x=s;now.w=0;hep_len=0;
put(now);
while(hep_len){
edge tmp=get();
int x=tmp.x;
if(vis[x])continue;vis[x]=1;
for(int i=0,sz=e[x].size();i<sz;++i)
if(!vis[e[x][i].x]&&dis[e[x][i].x]>dis[x]+e[x][i].w){
dis[e[x][i].x]=dis[x]+e[x][i].w;
now.x=e[x][i].x;now.w=dis[e[x][i].x];
put(now);
}
}
return ;
}
int main(){
freopen("traffic.in","r",stdin);
freopen("traffic.out","w",stdout);
fin>>N>>M>>T;
for(int i=1;i<N;i++)
for(int j=1;j<=M;j++) fin>>mp[0][i][j];
for(int i=1;i<=N;i++)
for(int j=1;j<M;j++) fin>>mp[1][i][j];
for(int i=1;i<N;i++)
for(int j=1;j<M;j++){
if(i!=N-1){add_edge(calc(i,j),calc(i+1,j),mp[1][i+1][j]);}
if(j!=M-1){add_edge(calc(i,j),calc(i,j+1),mp[0][i][j+1]);}
}
for(int i=1;i<N;i++){
add_edge(calc(i,1),(N-1)+(M-1<<1)+4+(N-1-i+1)+(N-1)*(M-1),mp[0][i][1]);
add_edge(calc(i,M-1),(M-1)+2+(i)+(N-1)*(M-1),mp[0][i][M]);
}
for(int j=1;j<M;j++){
add_edge(calc(1,j),j+1+(N-1)*(M-1),mp[1][1][j]);
add_edge(calc(N-1,j),(N-1)+(M-1)+3+(M-1-j+1)+(N-1)*(M-1),mp[1][N][j]);
}
while(T--){
fin>>K;
memset(G,63,sizeof G);memset(F,63,sizeof F);ans=0x3f3f3f3f;cnt=0;
for(int i=1;i<=K;i++) {int l,x,c;fin>>l>>x>>c;line[i][0]=x;line[i][1]=c;id[i]=i;line[i][2]=l;}
if(K<=1){fout<<0<<'\n';continue;}
sort(id+1,id+1+K,cmp);
for(int i=1;i<=K;i++){
int nx=i+1<=K?i+1:1;
if(line[id[i]][1]==line[id[nx]][1]){
for(int j=Nxt(line[id[i]][0]);j!=line[id[nx]][0];j=Nxt(j))update_edge(j+(N-1)*(M-1),Nxt(j)+(N-1)*(M-1),0);
update_edge(line[id[nx]][0]+(N-1)*(M-1),Nxt(line[id[nx]][0])+(N-1)*(M-1),line[id[nx]][2]);
}
else{
cnt++;
node[cnt][0]=Nxt(line[id[i]][0])+(N-1)*(M-1);node[cnt][1]=line[id[i]][1];
for(int j=Nxt(line[id[i]][0]);j!=line[id[nx]][0];j=Nxt(j))update_edge(j+(N-1)*(M-1),Nxt(j)+(N-1)*(M-1),0);
update_edge(line[id[nx]][0]+(N-1)*(M-1),Nxt(line[id[nx]][0])+(N-1)*(M-1),line[id[nx]][2]);
}
}
for(int i=1;i<=cnt;i++) {
dij(node[i][0]);
for(int j=1;j<=cnt;j++) if(i!=j) G[i][j]=min(G[i][j],dis[node[j][0]]);
}
for(int i=1;i<=cnt;i++)for(int j=1;j<=cnt;j++) G[i+cnt][j]=G[i][j+cnt]=G[i+cnt][j+cnt]=G[i][j];
for(int i=1;i<(cnt<<1);i++) F[i][i+1]=G[i][i+1];
for(int L=3;L<cnt;L+=2)
for(int i=1,j;i+L<=cnt;i++){
j=i+L;
for(int k=i+1;k<=j;k+=2)F[i][j]=min(F[i][j],F[i][k]+F[k+1][j]);
F[i][j]=min(F[i][j],G[i][j]+F[i+1][j-1]);
}
for(int i=1;i<=cnt;i++) ans=min(ans,F[i][i+cnt-1]);
fout<<(ans==0x3f3f3f3f?0:ans)<<'\n';
}
return 0;
}