8.8聯考題解
今天的T1讓我懷疑我是不是在做奧賽題……這考的是什麽知識點啊這個,會不會用絕對值函數?
Evensgn 的債務
時間限制: 1 Sec 內存限制: 128 MB
題目描述
Evensgn 有一群好朋友,他們經常互相借錢。假如說有三個好朋友A,B,C。A 欠 B 20 元,B 欠 C 20 元,總債務規模為 20+20=40 元。Evensgn 是個追求簡約的人,他覺得這樣的債務太繁雜了。他認為,上面的債務可以完全等價為 A 欠C20 元,B 既不欠別人,別人也不欠他。這樣總債務規模就壓縮到了 20 元。現在給定 n 個人和 m 條債務關系。Evensgn 想找到一種新的債務方案,使得每個人欠錢的總數不變,或被欠錢的總數不變(但是對象可以發生變化),並且使
輸入
輸入文件第一行兩個數字 n; m,含義如題目所述。
接下來 m 行,每行三個數字 ai; bi; ci,表示 ai 欠 bi 的錢數為 ci。
註意,數據中關於某兩個人 A 和 B 的債務信息可能出現多次,將其累加即可。
如”A 欠 B 20 元”、”A 欠 B 30 元”、”B 欠 A 10 元”,其等價為”A 欠 B 40 元”。
輸出
輸出文件共一行,輸出最小的總債務規模。
樣例輸入輸出
樣例輸入 1 5 3 1 2 10 2 3 1 2 4 1 樣例輸入 2 4 3 1 2 1 2 3 1 3 1 1
樣例輸出
樣例輸出 1 10 樣例輸出 2 0
數據範圍
對於 30% 的數據,1 ≤ n ≤ 10,1 ≤ m ≤ 10。
對於 60% 的數據,1 ≤ n ≤ 100, 1 ≤ m ≤ 104。
對於 80% 的數據,1 ≤ n ≤ 104,1 ≤ m ≤ 104。
對於 100% 的數據,1 ≤ n ≤ 106,1 ≤ m ≤ 106。
對於所有的數據,保證 1 ≤ ai; bi ≤ n; 0 < ci ≤ 100。
題解
不想寫題解……剛開始覺得,這是絕對值之和/2吧?弄了個環的例子繞一繞,好像還真沒什麽問題……後來閑得慌自己證明,發現用負數表示borrow,正數表示lend,正負數一消取絕對值正好是債務規模,似乎挺有道理?交題之前又有些猶豫,這題是考的什麽算法啊?還是交了,居然A了……A掉這道題,你需要掌握:循環語句、開數組、輸入輸出、調庫、絕對值函數(不會用cmath庫用if語句判斷取相反數也行),可以給下一屆高一當第一次考試題了;或許文化課一調的語句填空也行。昨天的第一題,雖然是原題好歹是題,還用了MP算法,但是這個,完全不懂意義何在。
include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int sj=1000010; int a[sj]={0},n,m,a1,a2,a3,ans=0; int r() { int jg=0,jk; jk=getchar()-‘0‘; if(jk<=9&&jk>0) jg=jk; jk=getchar()-‘0‘; while(jk>=0&&jk<=9) { jg=jg*10+jk; jk=getchar()-‘0‘; } return jg; } int main() { //freopen("t1.txt","r",stdin); n=r(); m=r(); for(int i=1;i<=m;i++) { a1=r(); a2=r(); a3=r(); a[a1]-=a3; a[a2]+=a3; } for(int i=1;i<=n;i++) ans+=abs(a[i]); ans/=2; printf("%d",ans); //while(1); return 0; }debt
Number
時間限制: 1 Sec 內存限制: 256 MB
題目描述
一個排列,求出了 a 數組,其中 ai 表示第 i 個數左邊有多少個數比它小
計算出原來的排列
輸入
第一行輸入 n 接下來 n - 1 個整數 ai,下標從 2 開始
輸出
輸出 n 個整數表示原排列
樣例輸入
5
1
2
1
0
樣例輸出
2
4
5
3
1
提示
對於 20% 的數據滿足:1 ≤ n ≤ 10
對於 50% 的數據滿足:1 ≤ n ≤ 1000
對於 100% 的數據滿足,1 ≤ n ≤ 100000
保證解存在
題解
這道題還是比較有意思的。剛開始啥都想不出來,先打了個dfs,又弄了個出數據的程序(走向對拍?)。發現dfs只能跑20分,有點難過。第三題看起來很不友好,第二題還拿不了分這是要完啊……然後寫寫畫畫,猛然發現這個最後一個好像是確定的,那倒數第二個呢,倒數第三個呢……這不就想出來了嗎,記錄一下每個數的temp(左邊可能有幾個比它小的),選出來一個就把比它大的都--,每次查哪一個數正好符合要求就行了。需要快速查詢,一開始想的是鏈表。打了個(n^2+2^n)/2的鏈表版本,出不了樣例,又轉去打n^2的,發現好像先選大的和先選小的還不一樣。感性地論證一下,較小數的temp更不容易變小,所以盡量先選小的。心懷疑慮地把鏈表版本改對,和1000的數據拍了一下發現能過,也就放心了。其實這個效率跑個10000也是綽綽有余的,但是出題人說給50就給50多一點分都沒有QAQ。第三題想不出來再回來看,要把n^2降下來,查詢得放在樹上,恍然大悟——原來是個平衡樹題啊。但是沒想到可以用有旋Treap查詢和刪除,被50分版本限制住了思路一直想著Splay或者無旋Treap的區間操作,Splay極容易打掛無旋Treap沒打過區間build函數不會寫,還是算了(早知道有旋Treap也可以做就打正解了嘛)。下午改題歡樂地拿有旋Treap輕松水過,delete函數寫錯一個字母又查了半天錯。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; const int sj=100010; int n,a[sj],ans[sj],size,root; struct Treap { int v,l,r,key,size; }t[sj]; void update(int x) { t[x].size=t[t[x].l].size+t[t[x].r].size+1; } void rturn(int &x) { int tt=t[x].l; t[x].l=t[tt].r; t[tt].r=x; t[tt].size=t[x].size; update(x); x=tt; } void lturn(int &x) { int tt=t[x].r; t[x].r=t[tt].l; t[tt].l=x; t[tt].size=t[x].size; update(x); x=tt; } void cr(int &x,int y) { if(x==0) { size++; x=size; t[x].size=1; t[x].v=y; t[x].key=rand(); return; } t[x].size++; if(y>t[x].v) { cr(t[x].r,y); if(t[t[x].r].key<t[x].key) lturn(x); } else { cr(t[x].l,y); if(t[t[x].l].key<t[x].key) rturn(x); } } int query(int x,int y) { if(x==0) return 0; if(t[t[x].l].size>=y) return query(t[x].l,y); if(t[t[x].l].size+1<y) return query(t[x].r,y-t[t[x].l].size-1); return t[x].v; } void sc(int &x,int y) { if(x==0) return; if(t[x].v==y) { if(t[x].l*t[x].r==0) x=t[x].l+t[x].r; else if(t[t[x].l].key<t[t[x].r].key) rturn(x),sc(x,y); else lturn(x),sc(x,y); } else if(y>t[x].v) t[x].size--,sc(t[x].r,y); else t[x].size--,sc(t[x].l,y); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) cr(root,i); for(int i=2;i<=n;i++) scanf("%d",&a[i]); ans[n]=a[n]+1; sc(root,ans[n]); for(int i=n-1;i>1;i--) { ans[i]=query(root,a[i]+1); sc(root,ans[i]); } ans[1]=t[root].v; for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }number
與非
時間限制: 2 Sec 內存限制: 256 MB
題目描述
作為一名新世紀共產主義的接班人,你認識到了資本主義的軟弱性與妥協性,決定全面根除資本主義,跑步邁入共產主義。但是當你即將跨入共產主義大門的時候,遇到了萬惡的資本家留下的與非電路封印,經過千辛萬苦的研究,你終於把復雜的破解轉變成了以下問題:
初始時你有一個空序列,之後有N個操作。
操作分為一下兩種:
1 x:在序列末尾插入一個元素x(x=0或1)。
2 L R:定義nand[L,R]為序列第L個元素到第R個元素的與非和,詢問nand[L,L]^nand[L,L+1]^nand[L,L+2]^......^nand[L,R]。
Nand就是先與,再取反
輸入
從文件nand.in中讀入數據。
輸入第一行一個正整數N,表示操作個數。
接下來N行表示N個操作。
為了體現程序的在線性,記lastans為上一次操作二的回答,初始lastans=0,。對於操作1,你需要對x異或lastans。對於操作二,設現在序列中的元素個數為M,如果lastans=1,那麽你需要作如下操作:L=M-L+1,R=M-R+1,swap(L,R)
輸出
輸出到nand.out中。
輸出有多行。為對於每一個操作二的回答。
樣例輸入
6
1 1
1 1
1 0
2 1 2
2 1 3
2 2 3
樣例輸出
1
0
0
提示
【數據規模和約定】
數據點 N的規模 操作一的個數M1 操作二的個數M2
1 N<=1000 M1<=500 M2<=500
2 N<=1000 M1<=500 M2<=500
3 N<=200000 M1<=100000 M2<=100000
4 N<=200000 M1<=100000 M2<=100000
5 N<=1000000 M1<=900000 M2<=100000
6 N<=4000000 M1<=3900000 M2<=100000
7 N<=4000000 M1<=3900000 M2<=100000
8 N<=4000000 M1<=3900000 M2<=100000
9 N<=4000000 M1<=3900000 M2<=100000
10 N<=4000000 M1<=3900000 M2<=100000
對於所有數據,滿足N<=4000000,M1<=3900000,M2<=100000,x={0,1},L<=R。
由於輸入規模較大,請使用較快的讀入方式以防讀入超時。
題解
%%%超哥。我承認我確實沒讀懂題,所以連20的暴力分都沒拿到。連續兩天T3爆零,很憂傷啊。超哥的思路完勝std!
f[i]=!(f[i-1]&a[i])
nand[i,j]=!(nand[i,j-1]&a[j])=!(……!(!(!(a[i]&a[i+1]))&a[i+2])……&a[j])
f[j]=!(f[j-1]&a[j])=!(……!(!(!(f[i-1]&a[i])&a[i+1]))&a[i+2])……&a[j])
唯一的差別在中間部分,真正的nand[i,j]是a[i]&a[i+1],而f[j]是(!(f[i-1]&a[i]))&a[i+1]
a[i+1]=0時前後都是0,一定相等
a[i+1]=1時若a[i]=0,前為0,後為1
若a[i]=1且f[i-1]=1,前為1,後為0
若a[i]=1但f[i-1]=0,前後還是相等的
對於每一個詢問的區間,從它有一位為0開始就可以套根據f數組計算出的前綴和sum了~至於前面的,暴力算吧。
其實也是想了想前綴和區間轉化之類的思路的,但是沒想出來……改題的時候想著亦或有可減性(亦或兩次變成1),本蒟蒻傻傻地打了sum[r]-sum[l-1]然後成功出了負數……最後批判這個題面一句:名字叫與非,題幹裏說先與再取反,雖然說大概能理解可是畢竟二進制還有個~運算叫取反啊……
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int sj=3900005; int r() { int jg=0,jk; jk=getchar()-‘0‘; if(jk<=9&&jk>0) jg=jk; jk=getchar()-‘0‘; while(jk>=0&&jk<=9) { jg=jg*10+jk; jk=getchar()-‘0‘; } return jg; } int n,a1,a2,op,s[sj],lans,ge,t,jg,f[sj],sum[sj],na[sj]; int main() { n=r(); sum[0]=1; for(int l=1;l<=n;l++) { op=r(); a1=r(); if(op==1) { a1^=lans; ge++; s[ge]=a1; f[ge]=!(f[ge-1]&s[ge]); sum[ge]=sum[ge-1]^f[ge]; if(ge==1) f[1]=s[1],sum[1]=s[1]; } if(op==2) { a2=r(); if(lans==1) { a1=ge-a1+1; a2=ge-a2+1; t=a1; a1=a2; a2=t; } jg=na[a1]=s[a1]; for(int j=a1+1;j<=a2;j++) { if(s[j]==1) { na[j]=!(na[j-1]&s[j]); jg^=na[j]; } else { jg^=sum[a2]; jg^=sum[j-1]; break; } } lans=jg; printf("%d\n",jg); } } return 0; }nand
8.8聯考題解