5 7 級 錯 位 打 ♂ 擊 賽
5 7 級 錯 位 打 擊 賽
本篇部落格並不會講任何演算法,所以極水
博主寫這篇部落格完全是出於在機房頹廢沒有事情做,順便整理一下模擬考試的題目,所以不會像之前那樣完善
T1
傳送門:https://www.luogu.com.cn/problem/U121235?contestId=31441
顯然,這是一個數據結構題(線段樹),但是由於考試的時候沒有推出標記下傳的方法來,所以寫了一個暴力,拿了40分的部分分
因為本人寫的程式碼實在太糟糕了(其實是懶得寫,反正有學長的詳細思路解釋,我就直接\(copy\)啦),所以以下是學長的T1題解
線段樹板子題沒人 \(AC\) 嘛?
前言
出題人本來打算給你們 \(40pts\)
本著信心賽的原則,我還是很希望你們能多拿一點分數,給教練一個肯定的回答,讓大家的分數好看點。
還是很希望你們能認真地聽完 \(30pts\) 的正解分。
當然現在我加強了資料,暴力只能拿 \(40pts\) 了吧。
\(10pts\)
對於 \(10\)% 的資料,\(x=0\) 。
操作 \(1\) 再怎麼花裡胡哨都是沒用的,我們只需要考慮操作 \(2\) 即可。
區間求和問題,可以直接用字首和來做,時間複雜度 \(Θ(n)\) 。
期望得分 \(:10pts\) 。
\(20pts\)
對於另外 \(10\)
只是多了一個單點修改的操作,只需注意我們在修改的時候是 \(-x\),可以用線段樹或樹狀陣列來做,時間複雜度 \(Θ(nlog_n)\) 。
期望得分 \(:20pts\) 。
\(40pts\)
對於另外 \(20\)% 的資料,\(1<=n,m<=5000\),\(-100<=a_i,x<=100\)。
考慮到 \(n\) 和 \(m\) 的範圍很小,我們直接用最樸素的演算法 "暴力" 直接做即可,時間複雜度 \(Θ(nm)\) 。
\(100pts\)
對於 \(100\)%的資料,保證 \(1<=n,m<=3*10^5\)
看到這個資料範圍,正解肯定是 \(Θ(nlog_n)\) 的資料結構無疑了 。
由於出題人沒有什麼好的做法,那麼這道題我們還是用線段樹做吧。
思考一下這道題和你們做的線段樹板子有什麼不同,很顯然它的操作 \(1\) 變得更加毒瘤且噁心。
但是換湯不換藥,線段樹板子的套路在這道題上還是可以應用的,那就是:懶標記
回顧一下區間加、區間求和的懶標記:
if(x<=l&&r<=y)
{
lazy[node]+=v;
sum[node]+=(r-l+1)*v;
return ;
}
比著葫蘆畫瓢,我們做這道題的時候也設定一個懶標記:
\(lazy1[node]=k\) 表示線段樹中 \(node\) 節點所代表的區間進行了一次操作 \(1\),要時刻注意操作 \(1\) 是以 \(-\) 開始的。
然後我們思考一下怎麼 \(Θ(1)\) 地對 \(sum[node]\) 進行維護:
找規律:我們可以將第 \(k\) 個和第 \(k+1\) 個看成一組,它們的貢獻之和為 \(x\) ,那麼所有組的貢獻總和為:\(x*\) 組數 :
但是可能還會存在一個落單的,別忘了計算這個落單的貢獻。
對於 \(lazy1[node]\) 的維護,我們直接令 \(lazy1[node]+=k\) 即可 。
這樣就可以了嘛?真 · 線段樹板子?上述做法有什麼問題嘛?
\(Problem1:\) 正負號問題 。
一個很重要的問題:我們上面的操作都是基於第一個數是 \(-\) 才行,也就是說,我們的操作形式要和題目中給出的操作 \(1\) 的形式一直才對 。
題目中的操作 \(1\) 不是以 \(-\) 的形式開始的嘛?為什麼當前區間第一個數可能會是 \(+\) 的形式?
如上圖所示:如果你要對 \([1,5]\) 進行操作 \(1\) ,當你遞迴到你的右兒子 \([4,5]\) 的時候發現是以 \(+\) 開頭的,但是由於左兒子的最左端就是它父親的最左端,所以左兒子開頭的正負性和父親開頭的正負性是相等的,我們比較好判斷,我們重點要判斷右兒子的開頭的正負性 。
想一想哈,如果左兒子的區間長度為 \(2k\),那麼右兒子開頭的正負性和父親的相同;如果左兒子的區間長度為 \(2k+1\),那麼右兒子開頭的正負性和父親的相反 。
\(Problem2:\) 標記下傳問題
注意一下我們 \(lazy1[node]=k\) 的定義:表示線段樹中 \(node\) 節點所代表的區間進行了一次操作 \(1\)。
注意到操作 \(1\) 是有兩個性質的:
①. 正負號交替;②. 每一項的絕對值之差為首項的絕對值。
注意到上圖右兒子中,操作 \(1\) 的首項為 \(+4x\) ,而每一項的絕對值之差為 \(x\),不符合操作 \(1\) 的定義了,所以我們不能直接維護懶標記。
提取公因式:我們將 \(+4x\) 看作是 \(+3x+x\),將 \(-5x\) 看作是 \(-3x-2x\),注意到右邊的 \(+x\) 和 \(-2x\) 了沒,這不就是操作 \(1\) 嘛?對於左邊的 \(+3x\) 和 \(-3x\) 呢,我們可以看出一種新的操作:
\(3.l,r,y\) 表示將 \([l,r]\) 的第 \(l\) 個數減 \(y\),第 \(l+1\) 個數加 \(y\),第 \(l+2\) 個數減 \(y\) ......
即:令 \(a_l=a_l-y\),\(a_{l+1}=a_{l+1}+y\),\(a_{l+2}=a_{l+2}-y\) ...... \(a_r=a_r+(-1)^{r-l+1}y\) 。
所以對於上面的右兒子,我們可以看作是先進行了一次 \(x=-x\) 的操作 \(1\) ,再加上一次 \(y=-y\) 的操作 \(3\) 。
對於操作 \(3\),我們同樣開一個懶標記 \(lazy2[node]=k\) 來進行維護,有 \(1\) 必有 \(2\) 嘛\(qwq\) 。
\(insert\)
void insert(int node,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y) //[l,r]被完全覆蓋
{
int len=r-l+1; //當前區間的長度
int d=l-x; //x~l-1之間有多少個數
if(d&1) //如果l前面有有奇數個數,說明第l個數在[x,y]內是第偶數個數,那麼應該是從加號開始
{
lazy1[node]-=k; //由於我們lazy1的設定是從減號開始的,所以這裡應該是-=
lazy2[node]-=k*d;
//將操作1進行拆解,所提取出來的公因數的絕對值為k*d,同樣我們操作3的設定也是從減號開始,所以這裡還是-=
sum[node]-=(len/2)*k; //對sum[node]進行維護,將它們兩兩看成一組,一共有len/2組,每一組的貢獻為-k
if(len&1) sum[node]+=k*(r-x+1);
//如果區間長度為奇數,說明兩兩一組有餘,由於我們已經判斷出該區間是從加號開始的,所以這個餘出的數一定是加
}
else //從減開始,與上面的同理,只是符號都改成相反的而已
{
lazy1[node]+=k;
lazy2[node]+=k*d;
sum[node]+=(len/2)*k;
if(len&1) sum[node]-=k*(r-x+1);
}
return ;
}
pushdown(node,l,r); //記得標記下傳
int mid=(l+r)>>1;
if(x<=mid) insert(node<<1,l,mid,x,y,k);
if(y>mid) insert(node<<1|1,mid+1,r,x,y,k);
update(node); //維護父親節點的sum
}
\(pushdown\)
如果你已經理解了 \(insert\) 的思路,那麼 \(pushdown\) 的思路也就不難了。
void pushdown(int node,int l,int r)
{
int mid=(l+r)>>1;
int len=r-l+1;
int len1=mid-l+1;
int len2=r-mid;
if(lazy1[node]) //時刻牢記lazy1是從減開始的
{
sum[node<<1]+=(len1/2)*lazy1[node]; //兩兩分組算貢獻
if(len1&1) sum[node<<1]-=len1*lazy1[node]; //兩兩分組有餘,那麼餘的這個數肯定是減的
lazy1[node<<1]+=lazy1[node]; //維護左兒子的lazy1
if(len1&1) //如果左兒子的區間長度為奇數,說明右兒子的左端點是第偶數個數,則是從加開始
{
lazy1[node<<1|1]-=lazy1[node]; //由於是從加開始,所以這裡是減
lazy2[node<<1|1]-=lazy1[node]*len1;
//只有右兒子不滿足操作1的性質②,因此我們要拆分操作,所以我們只維護右兒子的lazy2
sum[node<<1|1]-=(len2/2)*lazy1[node]; //兩兩分組算貢獻
if(len2&1) sum[node<<1|1]+=lazy1[node]*len; //兩兩分組有餘,由於右兒子是從加開始的,所以餘的這個數肯定是加的
}
else //右兒子的左端點從減開始,除了符號和上面都一樣
{
lazy1[node<<1|1]+=lazy1[node];
lazy2[node<<1|1]+=lazy1[node]*len1;
sum[node<<1|1]+=(len2/2)*lazy1[node];
if(len2&1) sum[node<<1|1]-=lazy1[node]*len;
}
lazy1[node]=0; //別忘了清空標記1
}
if(lazy2[node]) //下傳標記2
{
if(len1&1) sum[node<<1]-=lazy2[node]; //對於操作3,發現兩兩算貢獻的時候可以抵消,所以如果有餘肯定是減的
lazy2[node<<1]+=lazy2[node]; //維護左兒子的lazy2
if(len1&1) //右兒子的左端點從加開始
{
lazy2[node<<1|1]-=lazy2[node]; //維護右兒子的lazy2,由於右兒子的左端點是從加開始的,所以這裡是減
if(len2&1) sum[node<<1|1]+=lazy2[node];
}
else //右兒子的左端點從減開始,仍然只有符號不同
{
lazy2[node<<1|1]+=lazy2[node];
if(len2&1) sum[node<<1|1]-=lazy2[node];
}
lazy2[node]=0; //清空lazy2
}
}
完整版\(Code:\)
#include<iostream>
#include<cstdio>
using namespace std;
int read()
{
char ch=getchar();
int a=0,x=1;
while(ch<'0'||ch>'9')
{
if(ch=='-') x=-x;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
a=(a<<1)+(a<<3)+(ch-'0');
ch=getchar();
}
return a*x;
}
const int N=1e5+5;
int n,m;
int a[N],sum[N<<2],lazy1[N<<2],lazy2[N<<2];
void update(int node)
{
sum[node]=sum[node<<1]+sum[node<<1|1];
}
void pushdown(int node,int l,int r)
{
int mid=(l+r)>>1;
int len=r-l+1;
int len1=mid-l+1;
int len2=r-mid;
if(lazy1[node]) //時刻牢記lazy1是從減開始的
{
sum[node<<1]+=(len1/2)*lazy1[node]; //兩兩分組算貢獻
if(len1&1) sum[node<<1]-=len1*lazy1[node]; //兩兩分組有餘,那麼餘的這個數肯定是減的
lazy1[node<<1]+=lazy1[node]; //維護左兒子的lazy1
if(len1&1) //如果左兒子的區間長度為奇數,說明右兒子的左端點是第偶數個數,則是從加開始
{
lazy1[node<<1|1]-=lazy1[node]; //由於是從加開始,所以這裡是減
lazy2[node<<1|1]-=lazy1[node]*len1;
//只有右兒子不滿足操作1的性質②,因此我們要拆分操作,所以我們只維護右兒子的lazy2
sum[node<<1|1]-=(len2/2)*lazy1[node]; //兩兩分組算貢獻
if(len2&1) sum[node<<1|1]+=lazy1[node]*len; //兩兩分組有餘,由於右兒子是從加開始的,所以餘的這個數肯定是加的
}
else //右兒子的左端點從減開始,除了符號和上面都一樣
{
lazy1[node<<1|1]+=lazy1[node];
lazy2[node<<1|1]+=lazy1[node]*len1;
sum[node<<1|1]+=(len2/2)*lazy1[node];
if(len2&1) sum[node<<1|1]-=lazy1[node]*len;
}
lazy1[node]=0; //別忘了清空標記1
}
if(lazy2[node]) //下傳標記2
{
if(len1&1) sum[node<<1]-=lazy2[node]; //對於操作3,發現兩兩算貢獻的時候可以抵消,所以如果有餘肯定是減的
lazy2[node<<1]+=lazy2[node]; //維護左兒子的lazy2
if(len1&1) //右兒子的左端點從加開始
{
lazy2[node<<1|1]-=lazy2[node]; //維護右兒子的lazy2,由於右兒子的左端點是從加開始的,所以這裡是減
if(len2&1) sum[node<<1|1]+=lazy2[node];
}
else //右兒子的左端點從減開始,仍然只有符號不同
{
lazy2[node<<1|1]+=lazy2[node];
if(len2&1) sum[node<<1|1]-=lazy2[node];
}
lazy2[node]=0; //清空lazy2
}
}
void build(int node,int l,int r)
{
if(l==r)
{
sum[node]=a[l];
return ;
}
int mid=(l+r)>>1;
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
update(node);
}
void insert(int node,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y) //[l,r]被完全覆蓋
{
int len=r-l+1; //當前區間的長度
int d=l-x; //x~l-1之間有多少個數
if(d&1) //如果l前面有有奇數個數,說明第l個數在[x,y]內是第偶數個數,那麼應該是從加號開始
{
lazy1[node]-=k; //由於我們lazy1的設定是從減號開始的,所以這裡應該是-=
lazy2[node]-=k*d;
//將操作1進行拆解,所提取出來的公因數的絕對值為k*d,同樣我們操作3的設定也是從減號開始,所以這裡還是-=
sum[node]-=(len/2)*k; //對sum[node]進行維護,將它們兩兩看成一組,一共有len/2組,每一組的貢獻為-k
if(len&1) sum[node]+=k*(r-x+1);
//如果區間長度為奇數,說明兩兩一組有餘,由於我們已經判斷出該區間是從加號開始的,所以這個餘出的數一定是加
}
else //從減開始,與上面的同理,只是符號都改成相反的而已
{
lazy1[node]+=k;
lazy2[node]+=k*d;
sum[node]+=(len/2)*k;
if(len&1) sum[node]-=k*(r-x+1);
}
return ;
}
pushdown(node,l,r); //記得標記下傳
int mid=(l+r)>>1;
if(x<=mid) insert(node<<1,l,mid,x,y,k);
if(y>mid) insert(node<<1|1,mid+1,r,x,y,k);
update(node); //維護父親節點的sum
}
int query(int node,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return sum[node];
pushdown(node,l,r);
int mid=(l+r)>>1;
int cnt=0;
if(x<=mid) cnt+=query(node<<1,l,mid,x,y);
if(y>mid) cnt+=query(node<<1|1,mid+1,r,x,y);
return cnt;
}
int main()
{
//freopen("country.in","r",stdin);
//freopen("country.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++)
{
int opt,l,r,x;
opt=read();
l=read();r=read();
if(l>r) swap(l,r);
if(opt==1)
{
x=read();
insert(1,1,n,l,r,x);
}
else printf("%d\n",query(1,1,n,l,r));
}
return 0;
}
\(The\) \(last\)
線段樹是一種很優秀的資料結構,希望你們能多加練習,體會它的魅力。
本著出於讓你們練一練線段樹的目的,我才出的這道題,沒想到正解還真的不好做。出題人埋頭苦思想了 \(2.5h\) 才想出正解,當然可能有更優的方法等待著你們來挖掘。
最後膜一下你們這群 \(Θ(n^2)\) 過 \(1e4\) 資料的卡常帶師。
——暗ざ之殤
T2
講道理,憑良心說話,T2要比T1簡單億點
傳送門:https://www.luogu.com.cn/problem/U121369?contestId=31441
\(30pts\)
**首先感謝學長不殺之恩,看到資料範圍,瞭解到一個\(30pts\)的做法,因為\(n=1\),所以輸出\(0\)即可
\(100pts\)
題目描述的已經很清楚了,把問題抽象化出來,就是求節點\(1\)的單源最短路,再求所有節點到節點\(1\)的單源最短路,累加答案即可
首先,我們看到,求所有到\(1\),求\(1\)到所有第一個想到的解法應該是\(floyd\)吧
這個演算法可以在\(O(n^3)\)的時間內求出全源最短路,然後我們呼叫我們需要的資料,累加答案即可
但是顯然這不是正解,因為:
對於\(100\)%的資料,有\(n<=1000\)
\(1000^3\)你玩呢?所以我又想出瞭如下方案:
1、跑\(n\)遍堆優化的\(dijkstra\),然鵝,複雜度\(O(n*(m+n)logn)\),並沒有什麼實質上的優化
2、\(n\)遍\(spfa\),最壞複雜度\(O(n^2*m)\),依然是我們無法接受的量級
暫時沒有思路,所以我在小本子上把樣例畫了出來,發現了這樣一個有趣的現象:
(原諒我是靈魂畫手)
我們發現,如上圖(假設邊權都是\(1\)),我們跑一遍\(1\)->\(4\)的最短路,是\(1\)->\(3\)->\(4\),如果我們把邊反著建,再跑一邊最短路,就是\(1\)->\(4\),也就是正向圖\(4\)->\(1\)的最短路
所以我直接亂搞一波反向建邊+兩遍\(dijkstra\)(反正是樂多賽制可以實時觀測評測結果)
直接交上去發現,真就切掉了。。。
\(AC\) \(Code\)(後來我發現洛谷上還有一個幾乎一模一樣的題P1629,傳送門:https://www.luogu.com.cn/problem/P1629
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#define N 1010
using namespace std;
typedef long long int ll;
priority_queue< pair<int,int> >q;
ll mapp[N][N],dis[N],vis[N],cop[N],n,m,d,ans;
void upside_down(){
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
swap(mapp[i][j],mapp[j][i]);//這裡是反向建邊(我比較懶直接用的鄰接矩陣
}
void dijkstra(){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
q.push(make_pair(0,1));
while(q.size()!=0){
int x=q.top().second;
q.pop();
vis[x]=1;
for(int y=1;y<=n;y++){
int z=mapp[x][y];
if(dis[y]>dis[x]+z&&vis[y]!=1){
dis[y]=dis[x]+z;
q.push(make_pair(-dis[y],y));
}
}
}
}
int main(){
scanf("%lld%lld%lld",&n,&m,&d);
memset(mapp,0x7f,sizeof(mapp));
for(ll i=0;i<m;i++){
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
mapp[x][y]=min(z,mapp[x][y]);
}
for(int i=1;i<=n;i++)
mapp[i][i]=0;
dijkstra();
for(int i=1;i<=n;i++)
ans+=dis[i];
upside_down();
dijkstra();
for(int i=1;i<=n;i++)
ans+=dis[i];
printf("%lld",ans+(n-1)*d*2);
return 0;
}
T3
傳送門:https://www.luogu.com.cn/problem/U121284?contestId=31441
又雙叒叕是一個圖論題!
\(50pts\)
仍然先感謝學長不殺之恩:對於\(50\)%的資料,\(n\)<=100,所以我如果沒猜錯的話,這\(50pts\)應該是給了暴搜吧……但是好像機房裡沒有人願意拿這個部分分唉?
\(100pts\)
掃一眼題面,我們瞭解到,本題要求出從\(s\)->\(e\)的一條路徑,使得這條路徑上,最大的邊權最小,並輸出這個最大邊權最小值
想了一通有沒有這麼一個演算法來應對這種問題,,,但是顯然沒有
突然想到,本題的答案似乎具有單調性,可以使用二分法求解
具體怎麼個單調性?
首先,我們二分一箇中點\(Mid\),然後在圖上跑一遍,如果遇到權值比\(Mid\)值要大的邊,就自動忽視掉
然後,看看剩下的這些邊,能不能支援我們從\(s\)->\(e\)
如果不走比\(Mid\)權值大的邊,可以支援我們從\(s\)->\(e\),說明那個最大邊權最小值比\(Mid\)小,反之,比\(Mid\)大
然而還有一個問題,我們怎麼判斷這兩個點在上述二分求解的情況中是否被聯通呢?
一開始我想到了\(dfs\),但是經過我多年寫暴搜\(TLE\)的慘痛教訓,我真的不願意寫這個搜尋了。。。
於是我又想起了一個玄學做法——跑\(dijkstra\),首先賦值\(dis\)陣列為\(0x7f7f7f7f\),從\(s\)跑一遍\(dijkstra\),看看\(e\)的最短路有沒有被更新,如果被更新了,說明\(s\),\(e\)聯通,反之,\(s\),\(e\)不連通
然後我把這兩個玄學的思路結合到了程式碼裡:
\(Code\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N=5005;
typedef long long int ll;
struct edge{
int to,cost;
};
vector<edge>v[N];
priority_queue< pair<int,int> >q;
ll dis[N],vis[N],cop[N],n,m,s,e;
bool dijkstra(int a,int m){
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[a]=0;
q.push(make_pair(0,a));
while(q.size()!=0){
int x=q.top().second;
q.pop();
for(unsigned int i=0;i<v[x].size();i++){
int y=v[x][i].to,z=v[x][i].cost;
if(z>m) continue;
if(dis[y]>dis[x]+z){
dis[y]=dis[x]+z;
q.push(make_pair(-dis[y],y));
}
}
}
if(dis[e]>2147483646) return false;
else return true;
}
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=0,x,y,z;i<m;i++){
scanf("%lld%lld%lld",&x,&y,&z);
v[x].push_back((edge){y,z});
v[y].push_back((edge){x,z});
}
scanf("%lld%lld",&s,&e);
ll l=0,r=1000000,ans=1000000;
while(l<=r){
ll Mid=(l+r)>>1;
if(dijkstra(s,Mid)==true){
r=Mid-1;
ans=min(ans,Mid);
}else l=Mid+1;
}
printf("%lld",ans);
return 0;
}
結果\(AC\)了,比賽後學長髮了一下題解,正解是用\(Kruscal\)最小生成樹演算法做的,然後因為我不會\(Kruscal\)所以這裡直接貼一波\(std\)叭!
\(std\) \(Code\)
#include<iostream>
#include<cstdio>
#include<queue>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read()
{
int a=0,b=1;
char c=getchar();
while(!isdigit(c))
{
if(c=='-')
b=-1;
c=getchar();
}
while(isdigit(c))
{
a=(a<<3)+(a<<1)+(c^48);
c=getchar();
}
return a*b;
}
int num,s,t,maxn,father[5005];
struct edge
{
int from,to,dis;
}e[400005];
bool cmp(edge a,edge b)
{
return a.dis<b.dis;
}
int getfather(int x)
{
if(father[x]==x)
return x;
father[x]=getfather(father[x]);
return father[x];
}
int n,m,cnt;
long long ans;
int main()
{
// freopen("486.in","r",stdin);
// freopen("486.out","w",stdout);
int n=read(),m=read();
for(int i=1;i<=m;i++)
{
e[i].from=read(),e[i].to=read(),e[i].dis=read();
}
sort(e+1,e+m+1,cmp);
s=read(),t=read();
for(int i=1;i<=n;i++)
{
father[i]=i;
}
for(int i=1;i<=m&&cnt<=n-1;i++)
{
int fu=getfather(e[i].from),fv=getfather(e[i].to);
if(fu==fv)
continue;
father[fu]=fv;
cnt++;
ans+=e[i].dis;
if(getfather(s)==getfather(t))
{
printf("%d\n",e[i].dis);
return 0;
}
}
return 0;
}
##其實有T4,但是是一個毒瘤高精+動規,所以勞資不寫啦!!!