目錄:
————————————————————
ps:樓主腦殘有點嚴重,很容易寫錯別字和語言組織混亂,如果在讀文章時遇到,可以在評論區里回復一下,我好改(花式騙評論)
補題地址:http://acm.zju.edu.cn/onlinejudge/showProblems.do?contestId=1&pageNumber=31
順便好人做到底,給大家湊個11/13的題解出來。(據說A題題解某個大佬是有寫的,但是我找不到,有沒有好心人告訴我一下)
B:https://www.cnblogs.com/xseventh/p/9986194.html(摸魚的隊員終於把B題補掉了,dsu on tree)
C:https://blog.csdn.net/lzyws739307453/article/details/83932183 (分類討論)
D:https://blog.csdn.net/LSD20164388/article/details/83893264(枚舉+討巧)
E:https://blog.csdn.net/Code92007/article/details/83932020(二分+貪心)
F:http://www.cnblogs.com/qq965921539/p/9905086.html (構造題)
J :https://blog.csdn.net/Code92007/article/details/83930044(貪心)
M:傻逼簽到題,遞歸到1時判奇偶就行了
____________________________
K Airdrop
雖然K題現場過的比較少,但實際上是一個排個序 ,按順序枚舉x0,大力數數的垃圾題,只是現場榜歪沒人看而已。emmmm,根據重現的結果,可能k確實有一點難度,這里給一下提示,首先這個人啊,是優先先動y,再動x的,所以不和x=x0在同一條線上的點,只要與(x0,y0)的曼哈頓距離相同就有可能會撞。因此我們要在(x0,y0)的左右兩邊分別數每種曼哈頓距離出現的次數。顯然左右的是可以拆開數,所以我們先把所有x0左側曼哈頓距離相同的點的數量數完,在數右邊,最后在數一下多少個點的x=x0就行,如果你有注意到題目有說所有點的初始位置是不一樣的,可能會好寫很多。
現在以數左邊曼哈頓距離相同的點的數量為例,首先要注意到什么情況下曼哈頓距離相同點並不會抵消掉而是會留下一個,這里舉個栗子,當有三個點對於(x0,y0)的曼哈頓距離相同時,萬一有兩個因為比較早相遇而掛掉,則最后一個人就能活到空投那里。解決辦法也簡單,注意到,他們只能在y=y0這條直線上相遇,早相遇的點,他們相遇的x也比較小,只要在從左往右枚舉x0的過程中,順便把在上個x0的相撞死的人對於的數量貢獻去掉就好了。再維護一下當前有多少個曼哈頓距離只出現了1次,只出現一次的點都是能活着到空投的。
最后的最后,在教一下怎么數量,因為對於不同的x0,y0所有點的曼哈頓距離都在變的,我們不能每次重新數所有距離出現的次數。但是對於在(x0,y0)左側點,如果他們對於(x0,y0)曼哈頓距離相同,隨着(x0,y0)右移他們還是會相同的,所以干脆直接數他們到(xm,y0)的曼哈頓距離就行了,xm為x0坐標的最大值。 他們到(xm,y0)距離相同的話,則這些點到任意他們的右側(x0,y0)距離也肯定相同。

1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<stdlib.h> 5 #include<vector> 6 #include<algorithm> 7 using namespace std; 8 int const maxn=2e5+5; 9 int x[200005]; 10 int y[200005]; 11 vector<int>e[maxn],q,que,res; 12 int sum[200005]; 13 int num[200005]; 14 void clear(int n,int x0,int y0) 15 { 16 for(int i=1;i<=n;i++) 17 { 18 num[abs(x0-x[i])+abs(y0-y[i])]=0; 19 } 20 } 21 int main() 22 { 23 int i,j,n,m,w,t,y0,xm=1e5+1,k; 24 scanf("%d",&t); 25 while(t--) 26 { 27 scanf("%d%d",&n,&y0); 28 q.clear(); 29 q.push_back(0); 30 // e[0].clear(); 31 for(i=1;i<=n;i++) 32 { 33 scanf("%d%d",&x[i],&y[i]); 34 e[x[i]].clear(); 35 e[x[i]+1].clear(); 36 // e[x[i]-1].clear(); 37 q.push_back(x[i]); 38 q.push_back(x[i]+1); 39 // q.push_back(x[i]-1); 40 } 41 sort(q.begin(),q.end()); 42 q.erase(unique(q.begin(),q.end()),q.end()); 43 for(i=1;i<=n;i++) 44 { 45 e[x[i]].push_back(y[i]); 46 } 47 int ans=0,temp=0; 48 clear(n,xm,y0); 49 for(int xx:q) 50 { 51 sum[xx]=e[xx].size(); 52 if(temp) 53 { 54 que.clear(); 55 for(int yy:e[temp]) 56 { 57 k=abs(yy-y0)+abs(xm-temp); 58 num[k]++; 59 if(num[k]==1) 60 { 61 ans++; 62 } 63 else 64 { 65 que.push_back(k); 66 } 67 } 68 for(int pos:que) 69 { 70 if(num[pos]) 71 ans--; 72 num[pos]=0; 73 } 74 } 75 temp=xx; 76 sum[xx]+=ans; 77 // printf("(%d %d)\n",xx,sum[xx]); 78 } 79 reverse(q.begin(),q.end()); 80 81 ans=0,temp=0; 82 clear(n,0,y0); 83 res.clear(); 84 for(int xx:q) 85 { 86 if(temp) 87 { 88 que.clear(); 89 for(int yy:e[temp]) 90 { 91 k=abs(yy-y0)+abs(0-temp); 92 num[k]++; 93 if(num[k]==1) 94 { 95 ans++; 96 } 97 else 98 { 99 100 que.push_back(k); 101 } 102 } 103 for(int pos:que) 104 { 105 if(num[pos]) 106 ans--; 107 num[pos]=0; 108 } 109 } 110 temp=xx; 111 sum[xx]+=ans; 112 res.push_back(sum[xx]); 113 //printf("(%d %d %d)\n",xx,sum[xx],ans); 114 } 115 sort(res.begin(),res.end()); 116 printf("%d %d\n",res.front(),res.back()); 117 } 118 return 0; 119 }
I Soldier Game
這題兩種寫法,一種是枚舉最小值+線段樹單點更新維護可行最大值。 另一種是dp (神仙知道怎么寫,我不知道)。這題思路還是比較復雜了,然后HDU的CSY巨巨現場一眼標算,果然神仙和凡人是不能比的。
當我問群巨怎么做時,他們只給我“枚舉最小值+線段樹維護可行最大值“,這幾個關鍵字,我百思不得其解,終於想了幾天后頓悟了。o(╥﹏╥)o
首先。我們考慮一個動態規划問題:給出a個長度為1的區間和b個長度為2區間,每個區間都有權重。從中取出若干個不相交的區間,在滿足覆蓋滿[1,n]情況下,使得【權重最大的區間】的權重最小,要怎么做。
一個顯然的想法:令dp[i]代表覆蓋滿[1,i]的所有方案中,權重的最大值最小為多少。然后按左端點從小到大枚舉區間,進行狀態轉移就行了,復雜度O(a+b)。
然后現在我就可以得到I題的一個O(n^2)寫法了,具體做法是這樣的:
我們把n個長度為1的區間和n-1長度為2的區間按權值排序 ->枚舉最小值 -> 將權值小於最小值的區間刪去 ->
-> 然后對剩下的區間做dp,求出可行的【最小的最大值】,計算最大值和當前枚舉的最小值之差,並更新答案。
上面那個做法雖然不夠優越,但是已經為正確的做法已經鋪好了路,因為上面涉及到一個線性dp的單點修改后在查詢,套個老掉牙線段樹維護dp值不就可以在log(n)的時間內查詢到修改后的值了嗎。
所以現在我們就有了個nlog(n)做法,核心在於怎么把dp掛到線段樹上。不過說起來容易做起來難, 因為你發現如果你要搬到線段樹上要維護一坨東西才行,
不過核心思想並不復雜,只是枚舉一下樹上孩子合並時是否有區間跨過兩個孩子就行了,合並操作的具體代碼長這樣
1 struct Node 2 { 3 ///m代表中間,l代表左,r代表右 4 int rl; 5 int m;///只包含中間,不包含區間的左右端點 6 int mr;///包含中右 7 int lm;///包含左中 8 int lmr;///包含左中右 9 int set(int i) 10 { 11 rl=a[i][1]; 12 lm=mr=-INF; 13 lmr=a[i][0]; 14 m=INF; 15 } 16 inline Node operator+(const Node &b)const 17 { 18 Node ans; 19 ans.m=min(max(mr,b.lm),max(max(m,rl),b.m)); 20 ans.lm=min(max(lmr,b.lm),max(max(lm,rl),b.m)); 21 ans.mr=min(max(mr,b.lmr),max(max(m,rl),b.mr)); 22 ans.lmr=min(max(lmr,b.lmr),max(max(lm,rl),b.mr)); 23 ans.rl=b.rl; 24 return ans; 25 } 26 27 };
有沒有被嚇到,不過努力思考一下應該還是看得懂的。
完整的AC代碼如下(我刪掉一個區間的方法,是把這個區間權值賦值成無窮大,這樣對答案就沒有貢獻了,話說我居然是跑得最快的╰(*°▽°*)╯):

1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include<math.h> 5 using namespace std; 6 typedef long long ll; 7 #define N 280000 8 int a[200005][2]; 9 const int INF=(1<<31)-1; 10 struct Node 11 { 12 ///m代表中間,l代表左,r代表右 13 int rl; 14 int m;///只包含中間,不包含區間的左右端點 15 int mr;///包含中右 16 int lm;///包含左中 17 int lmr;///包含左中右 18 int set(int i) 19 { 20 rl=a[i][1]; 21 lm=mr=-INF; 22 lmr=a[i][0]; 23 m=INF; 24 } 25 inline Node operator+(const Node &b)const 26 { 27 Node ans; 28 ans.m=min(max(mr,b.lm),max(max(m,rl),b.m)); 29 ans.lm=min(max(lmr,b.lm),max(max(lm,rl),b.m)); 30 ans.mr=min(max(mr,b.lmr),max(max(m,rl),b.mr)); 31 ans.lmr=min(max(lmr,b.lmr),max(max(lm,rl),b.mr)); 32 ans.rl=b.rl; 33 return ans; 34 } 35 36 }; 37 const int MAXN=280000; 38 struct Segment_tree 39 { 40 int size; 41 Node node[MAXN]; 42 void update(int pos) 43 { 44 node[pos]=node[pos+pos]+node[pos+pos+1]; 45 } 46 void build(int n)///申請空間 47 { 48 size=1; 49 while(size<n)///計算幾個剛好能套住整個區間的區間容量 50 size<<=1;///size=size*2 51 // printf("size=%d\n",size); 52 } 53 void build(int n,int flag)///申請空間,並建樹 54 { 55 build(n); 56 int i; 57 for(i=n; i<size; i++) 58 { 59 a[i][0]=-INF; 60 a[i][1]=INF; 61 } 62 for(i=0; i<size; i++) 63 { 64 node[size+i].set(i); 65 } 66 for(i=size-1; i>0; i--) 67 { 68 update(i);///數值從低到高更新上去 69 } 70 } 71 void change(int x) 72 { 73 x+=size; 74 node[x].set(x-size); 75 while(x>1) 76 { 77 // printf("[%d]\n",x); 78 x>>=1; 79 update(x); 80 } 81 } 82 void put() 83 { 84 int i=1,j=1,s=size*4,k,len=3; 85 for(i=1; i<=2*size-1; i++) 86 { 87 if(i==j) 88 { 89 puts(""); 90 j<<=1; 91 s>>=1; 92 for(k=0; k<len*(s/2-1); k++) 93 printf(" "); 94 } 95 printf("%3d",node[i].lmr); 96 for(k=0; k<len*(s-1); k++) 97 printf(" "); 98 } 99 puts(""); 100 } 101 102 } tree; 103 typedef pair<int,int> PI; 104 PI b[200005]; 105 int cmp(const PI &x,const PI &y) 106 { 107 return a[x.first][x.second]<a[y.first][y.second]; 108 } 109 int main() 110 { 111 int n,m,t,i; 112 //freopen("1.in","r",stdin); 113 scanf("%d",&t); 114 while(t--) 115 { 116 scanf("%d",&n); 117 int top=0; 118 for(i=0; i<n; i++) 119 { 120 scanf("%d",&a[i][0]); 121 b[top++]=make_pair(i,0); 122 if(i>0) 123 { 124 a[i-1][1]=a[i-1][0]+a[i][0]; 125 b[top++]=make_pair(i-1,1); 126 } 127 } 128 a[n-1][1]=INF; 129 sort(b,b+top,cmp); 130 tree.build(n,1); 131 int j=0; 132 int mn; 133 long long ans=INF*2LL; 134 for(i=0; i<top;) 135 { 136 mn=a[b[i].first][b[i].second]; 137 // printf("i=%d\n",i); 138 139 if(tree.node[1].lmr==INF) 140 { 141 break; 142 } 143 ans=min(ans,tree.node[1].lmr-(long long)mn); 144 while(j<top&&a[b[j].first][b[j].second]<=mn) 145 { 146 a[b[j].first][b[j].second]=INF; 147 tree.change(b[j].first); 148 j++; 149 } 150 i=j; 151 } 152 153 printf("%lld\n",ans); 154 } 155 return 0; 156 }
L Sub-cycle Graph
好吧,這是一個比較傻吊的組合數學題,但是現場的時候看錯條件了,越想越復雜_(¦3」∠)_。
題意是問有多少種n個點m條邊的圖,是一個連通簡單環圖的子圖。
首先因為是連通簡單環圖,所以母圖必須只有一個簡單環且連通所有結點,在斷掉一些邊后,必然會變成n-m條鏈的組合。
現在我們先來考慮一個比較簡單的問題:n個結點組成一條鏈有多少種本質不同的方案
顯然這個數列的前n項是1,1,3,12,……,n!/2。
寫成Σai/(i!) x^i 的指數型生成函數的話(什么你不會指數型生成函數,那還不趕緊去學),就是
則組成k條鏈的指數型生成函數就是
則題目想要的答案就是bn /k!。 為啥除k! ? 因為我們的答案是不考慮這k條鏈出現的先后順序的。
然后開始FFT?, 答案是NO!
上面那個多項式我們並不需要真的做多項式的冪運算。首先上面的式子可以拆成這樣。
其中
可以用二項式系數直接展開。
而
是一個特殊的多項式,與這個多項式做卷積相當於對系數求前綴和。
乘k次,就相當於求k次前綴和,求k次前綴和的第m項,想必現在是個人都應該會了。
所以到此題目就做完了,算法復雜度O(m)

1 #include<stdio.h> 2 #include<algorithm> 3 using namespace std; 4 #define ll long long 5 ll fac[100005],ifac[100005]; 6 const ll mod=1e9+7,inv2=(mod+1)/2; 7 ll C(int n,int m) 8 { 9 if(m>n) 10 return 0; 11 return fac[n]*ifac[m]%mod*ifac[n-m]%mod; 12 } 13 int main() 14 { 15 int n=1e5+2,m,t,i,j,k; 16 ifac[1]=1; 17 ifac[0]=1; 18 for(i=2; i<=n; i++) 19 { 20 ifac[i]=(mod-mod/i)*ifac[mod%i]%mod; 21 } 22 fac[0]=1; 23 for(i=1; i<=n; i++) 24 { 25 fac[i]=fac[i-1]*i%mod; 26 ifac[i]=ifac[i-1]*ifac[i]%mod; 27 } 28 scanf("%d",&t); 29 while(t--) 30 { 31 scanf("%d%d",&n,&m); 32 if(m>n) 33 { 34 puts("0"); 35 } 36 else if(m==n) 37 { 38 printf("%lld\n",fac[n-1]*inv2%mod); 39 } 40 else 41 { 42 ll ans=0,temp=1; 43 k=n-m; 44 for(i=0;i<=m;i++) 45 { 46 // printf("[%d %d],%d\n",k,m-i,C(k+(m-i)-1,m-i)); 47 ans+=C(k,i)*temp%mod*C(k+(m-i)-1,m-i)%mod; 48 ans%=mod; 49 temp=temp*-inv2%mod; 50 if(temp<0) 51 temp+=mod; 52 } 53 printf("%lld\n",ans%mod*fac[n]%mod*ifac[k]%mod); 54 } 55 56 } 57 return 0; 58 }
G Repair the Artwork
問題1:
只有0和1情況方案數怎么計算?
在只有0,1的時候,可行方案數顯然 【可選的區間數】^m
問題2:
只有2的情況方案數怎么計算:
為了方便表示,我們約定一下記號(不然說起來會像繞口令)
- 如果一個位置上的2被當做1用(即一定不覆蓋這個位置)我們把用×表示這個2,
- 如果一個位置上的2被當做0用(即這個位置可能被覆蓋,也可能沒有被覆蓋)我們把用O表示這個2。
- 如果一個位置上的2一定被覆蓋用Θ表示,
- {??……?} 表示每個2滿足??……?約束情況下的方案數。
現在我們來推導一下只有兩個2時方案數怎么計算
則我們要求的答案{ΘΘ},隨便容斥一下就可展開成如下形式:
(注:如果你實在看不懂,我再解釋一下,{OΘ}可以拆成第一個位置【一定被被覆蓋】和【一定不被覆蓋】兩種情況,所以就有了{OΘ}={ΘΘ}+{×Θ} =>{ΘΘ}={OΘ}-{×Θ}. 接着同理對剩下的Θ,用相同的方式展開掉就行了。}
這樣就轉化為只有01的形式,然后用問題的1方式就能計算了,到這問題就解決了嗎?然而還有一些計算上的細節問題要解決。
所以我們繼續嘗試計算{ΘΘΘ},來說明這個問題
經過一番費力的展開,你會得到{ΘΘΘ}={OOO} - {OO×} - {O×O} + {O××} - {×OO} + {×O×} + {××O} -{×××} .
你觀察一下就會發現,實際上發現,其實就是
其中r(A1,A2,A3) 為A1,A2,A3中x的個數。 說白就是類似多項式(1+(-x))^m 的展開形式。
在這里我們有兩個重要的結論:
- 結論一:答案一定能表示如下形式(因為任意一個{A1,A2,A3}肯定是x^m 形式)。
- 結論二:暴力枚舉情況,要枚舉2^n種可能,所以暴力是不可行的
問題3:在只有2的情況下,怎么在多項式時間里計算n個2的方案數?
根據問題2的結論1,對dp初步方案是用dp[i][j],代表{i個Θ}中 j^m的系數。 然后答案就是Σdp[i][j]* j^m,
然后來考慮一下轉移:{i個Θ}={(i-1個Θ) +O}-{i-1個Θ) +×}。
你自行思考一下就會發現,這個{ (i-1個Θ) +×} 不就是等於{i-1個Θ} 嗎? 因為這個尾巴上的×,對可選區間數莫有蝦米貢獻,(因為x是不能選的,掛不掛在尾巴上沒差)。
而這個(i-1個Θ) +O} 好像不太能轉化成和{i-1個Θ}有關形式,因為每種情況的可選區間數,都可以額外增加【最后一個O貢獻的可選區間的數】=【右端點為n的可選區間的數量】=【i - 上一個1的位置】。但是這個上一個1的位置,對於不同情況是不一樣的。(例如{OO}=3^m 而再加個O話,{OOO}=(3+3)^m ,而對於{O×}=1^m,再加個O,{O×O}=(1+1)^m,)
因此我還需要對dp狀態加個維度,我們用dp[i][j][k]代表{i個Θ}展開中,最后一個×出現的位置為j的方案中 k^m的系數。 然后答案就是Σdp[i][j][k]* k^m,
現在我在來考慮一下dp方程這么轉移。我們要加上{(i-1個Θ) +O}的方案數,減掉{i-1個Θ) +×}的方案數,所以有:
- 當尾巴加O不改變最后一個1位置,但是改變可選區間數:
for(j=0,j<i;j++)
- 當尾巴加x時,所有轉移到這個狀態的,【最后一個1的位置】都要改成i:
到此轉移就完成了,然后你會發現,這個算法是n^4。但是常數很小,然后題目有1000組數據,其中包含50組大數據_(¦3」∠)_,寫挫就可能慘遭卡常。
順便給個樣例,100個2 ,m=1e9 的方案數mod 1e9+7 的結果是388544367。
問題四 在0,1,2三種數字都考慮的情況,狀態該怎么轉移?。
其實0,1轉移方法和上面類似的,
- 所以遇到1,因為{(i-1個Θ) +1}={(i-1個Θ) } ,所以連動都不要動,但是注意需要記這個1的位置,因為在計算右端點為n的可選區間的數量會用到。
- 而遇到0時,所有情況下k那個維度都要加上【i - 上一個1的位置】偏移量,所以要討論一下這個1是從2變來,還是真正的1。
- 然后遇到2,轉移是和問題三一樣的,只不過也要注意一下,計算偏移量時的這個1是從2變來,還是真正的1就行了
最后,你發現這個毒瘤題還會有爆內存的風險,需要優化掉一維的空間。而且可能還要和卡常斗智斗勇。
所以再仔細觀察一下,你發現這兩種轉移其實就是兩種簡單的操作:一種是數組下標偏移,一種是求j那維的前綴和。
因此當個二維數組寫的時候:遇到1,不做任何操作。遇到0時只累加偏移量。遇到2,枚舉j求個前綴和,求玩在計算2帶來的偏移量。
最后在計算答案的時候,把偏移量代入就行了
AC代碼如下:

1 #include<cstdio> 2 #include<algorithm> 3 #include<string.h> 4 #include<vector> 5 using namespace std; 6 #define ll long long 7 const int mod=1e9+7; 8 inline int max(const int &x,const int &y) 9 { 10 return x>y?x:y; 11 } 12 int dp[105][6005]; 13 int d[105]; 14 int xx[105]; 15 int a[105]; 16 ll temp[6005]; 17 inline ll quickpow(ll a, ll n) 18 { 19 ll ans=1; 20 while(n) 21 { 22 if(n&1) 23 ans=ans*a%mod; 24 n>>=1; 25 a=a*a%mod; 26 } 27 return ans; 28 } 29 int main() 30 { 31 int n,m,t,r,i,mx; 32 scanf("%d",&t); 33 while(t--) 34 { 35 scanf("%d%d",&n,&m); 36 r=0; 37 mx=0; 38 a[0]=2; 39 for(i=1; i<=n; i++) 40 { 41 scanf("%d",&a[i]); 42 if(a[i]==1) 43 { 44 r=i; 45 } 46 mx+=i-r; 47 } 48 for(i=0; i<=n; i++) 49 memset(dp[i],0,sizeof(dp[i][0])*(mx+1)); 50 memset(xx,0,sizeof(xx)); 51 memset(d,0,sizeof(d)); 52 dp[0][0]=1;///注意啥都不選也有一種方案。 53 r=0; 54 mx=0; 55 for(i=1; i<=n; i++) 56 { 57 if(a[i]==1)///1的時候屁事都不干 58 { 59 r=i; 60 } 61 else 62 { 63 if(a[i]==0)///0的時候累加偏移量 64 { 65 for(int j=0; j<i; j++) 66 { 67 xx[j]+=i-max(j,r); 68 } 69 } 70 else if(a[i]==2) 71 { 72 for(int j=0; j<i; j++) 73 { 74 if(a[j]==2) 75 { 76 mx=d[j]; 77 d[i]=max(d[i],mx+xx[j]);///d[i]是可能有值的系數的寬度。 78 for(register int k=xx[j]; k<=mx+xx[j]; k++)///這里注意一下j帶有的偏移量,所以不能直接 dp[i][k]-=dp[j][k],而是應該dp[i][k]-=dp[j][k-xx[j]];, 79 { 80 dp[i][k]-=dp[j][k-xx[j]]; 81 if(dp[i][k]<0) 82 dp[i][k]+=mod; 83 } 84 } 85 } 86 for(int j=0; j<i; j++)///把2當0時累加偏移量。注意這個for是不能和上一個for交換位置,如果寫個三維數組的,再改成二維數組就會知道為什么。 87 { 88 xx[j]+=i-max(j,r); 89 } 90 } 91 } 92 } 93 long long ans=0; 94 mx=0; 95 for(i=0; i<=n; i++) 96 { 97 mx=max(mx,d[i]+xx[i]); 98 } 99 for(i=1; i<=mx; i++)///預處理一下所有數的次冪。 100 { 101 temp[i]=quickpow(i,m); 102 } 103 for(int i=0; i<=n; i++) 104 { 105 if(a[i]==2) 106 { 107 for(int j=0; j<=d[i]; j++) 108 { 109 ans+=dp[i][j]*temp[j+xx[i]]%mod; 110 ans%=mod; 111 } 112 } 113 } 114 if(ans<0) 115 ans+=mod; 116 printf("%lld\n",ans); 117 } 118 return 0; 119 }