1. 程式人生 > >Codeforces Round #353 (Div. 2) ABCDE 題解 python

Codeforces Round #353 (Div. 2) ABCDE 題解 python

Problems
#Name
A standard input/output 1 s, 256 MB Submit Add to favourites  x3509
B standard input/output 1 s, 256 MB Submit Add to favourites  x2519
C standard input/output 1 s, 256 MB Submit Add to favourites  x724
D standard input/output 2 s, 256 MB Submit Add to favourites  x1008
E standard input/output 2 s, 256 MB Submit Add to favourites  x239

以後cf的題能用python寫的我就python了,因為以後沒正式比賽參加了,不必特地用C++。python寫得快,也容易看得懂,我最近也比較需要練習這個。當然有的題C++寫得少我還是用C++。

A. Infinite Sequence

題意:給出a,b,c,求是否a加若干個c能得到b,是就輸出YES,否就輸出NO

題解:

就特判各種情況,一般情況是看(b-a)%c==0

特殊情況,依次判斷:

1.a==b,YES

2.c==0,NO

3.b-a與c不同號,NO

4.c小於零,則把b-a和c都變正數再判。

 1 def
gank(a,b,c): 2 d = b - a 3 if(d==0): 4 return True 5 if(c==0): 6 return False 7 if((d<0 and c>0) or(c<0 and d>0)): 8 return False 9 if(c<0): 10 d*=-1 11 c*=-1 12 if(d%c==0): 13 return True 14 else
: 15 return False 16 17 a,b,c = map(int , raw_input().split(' ')) 18 if(gank(a,b,c)): 19 print "YES" 20 else: 21 print "NO"
View Code

B. Restoring Painting

題意:有個3*3的九宮格,每個格子能填1~n中任意的數(n由輸入給出)。要求其中任意2*2的格子中4個數的和與其他各個2*2格子都相等。

輸入n,a,b,c,d,求剩下的數有多少種填法。(可能為0種)

題解:

固定中間的為1,則4種2*2格子的和,要是使得一個相鄰的數比較大的角為1,另一個相鄰數字比較小的角為n,格子和也沒法相等的話,就不行,所以要找2*2格子的最小值和最大值,判斷可行性。

比如有這種情況,最大那個角填1,最小那個角最少只能填x,則它們有(n-x+1)種情況(最小的那個角為x,為x+1,直到為n)。

中間那個數其實隨便填,不影響,所以最後答案(n-x+1)*n

 1 def gank(n,A,b,c,d):
 2     a = [0]*4
 3     a[0] = A+b
 4     a[1] = A+c
 5     a[2] = b+d
 6     a[3] = c+d
 7     mi = 1e9
 8     ma = 0
 9     for i in range(4):
10         ma = max(ma,a[i])
11         mi = min(mi,a[i])
12     if(1 + 1 + ma > 1+n+mi):
13         return 0
14     x = ma - mi + 1
15     y = n - x + 1
16     return y*n
17 
18 
19 n,a,b,c,d = map(int , raw_input().split(' '))
20 print gank(n,a,b,c,d)
View Code

C. Money Transfers

題意:

有一圈銀行,瓦夏在各個銀行存的錢為a[i](可能為負數,代表借了錢),sum(a[i])==0,瓦夏可以進行一種操作:把一個銀行的若干錢轉到相鄰的銀行。求最少多少次操作能把所有銀行存款歸零。

題解:

這題,難!過D的人都比過C的多,我是不會的,看的題解。

首先考慮,若有一個區間[L,R],使得其中的sum(a[i])==0,則這個區間可以單獨轉錢就能歸零,用的運算元為R-L。如果一個銀行為0,它可以單獨當一個區間。最後,我們可以得到若干個相鄰的區間,總運算元為(n - 區間數)。

所以問題轉化為最大化這種區間數。

為了找到和為0的區間,我們算一波字首和。

當有兩個位置的字首和相同,說明這兩個之間的各個元素和為0!

當很多個位置的字首和相同,說明這些位置分成的各個區間,每個區間和為0。

我們就算一波各個字首和出現的次數,出現次數最多的那個就是按照最碉的分割槽間法得到的最多區間數。

 1 def farm(n, a):
 2     dic = dict()
 3     re=0
 4     sum = 0
 5     for i in a:
 6         sum += i
 7         if not sum in dic:
 8             dic[sum]=0
 9         dic[sum]+=1
10         re = max(re,dic[sum])
11     return n - re
12 
13 n = input()
14 a = map(int, raw_input().split(' '))
15 ans = farm(n, a)
16 print ans
View Code

D. Tree Construction

題意:給出一個各不相同的序列,插入二叉搜尋樹中,二叉搜尋樹不作平衡處理,直接強插,輸出除了第一個點之外各個點的父親的值。

題解:

直接強插,O(n^2),會爆。我不懂,我又看的題解會的。

這個樸素二叉搜尋樹的特性,是我要插x,那它肯定要成為之前插入過的數中比它小的中最大的數的右兒子 或者 比它大的數中最小的數的左兒子。

所以我們就找用別的平衡樹找到這2個數在樸素樹中的位置。然後根據性質,肯定只有一個地方能插,我們就插。(可惡,我不懂為什麼,對這個樹的性質理解不完全)

可以用C++的STL的set和map來當平衡樹,我就用C++寫了。

 1 //#pragma comment(linker, "/STACK:102400000,102400000")
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<iostream>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 #include<set>
10 #include<stack>
11 #include<queue>
12 using namespace std;
13 
14 #define MZ(array) memset(array, 0, sizeof(array))
15 #define MF1(array) memset(array, -1, sizeof(array))
16 #define MINF(array) memset(array, 0x3f, sizeof(array))
17 #define REP(i,n) for(i=0;i<(n);i++)
18 #define FOR(i,x,n) for(i=(x);i<=(n);i++)
19 #define FORD(i,x,y) for(i=(x);i>=(y);i--)
20 #define RD(x) scanf("%d",&x)
21 #define RD2(x,y) scanf("%d%d",&x,&y)
22 #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)
23 #define WN(x) printf("%d\n",x);
24 #define RE  freopen("D.in","r",stdin)
25 #define WE  freopen("huzhi.txt","w",stdout)
26 #define MP make_pair
27 #define PB push_back
28 #define PF push_front
29 #define PPF pop_front
30 #define PPB pop_back
31 template<class T>inline void OA(const T &a,const int &st,const int &ed) {
32     if(ed>=st)cout<<a[st];
33     int i;
34     FOR(i,st+1,ed)cout<<' '<<a[i];
35     puts("");
36 }
37 typedef long long LL;
38 typedef unsigned long long ULL;
39 
40 const double PI=acos(-1.0);
41 const double EPS=1e-10;
42 const int MAXN=111111;
43 const int MAXM=33;
44 
45 struct Node {
46     int value;
47     Node *son[2];
48     Node() {}
49     Node(int v) {
50         value = v;
51         son[0]=son[1]=NULL;
52     }
53 } root;
54 
55 typedef pair<int, Node*> PIN;
56 set<PIN> s;
57 int n;
58 int a[MAXN];
59 int ans[MAXN];
60 
61 void farm() {
62     root = Node(a[0]);
63     s.clear();
64     s.insert(MP(a[0], &root));
65     int i;
66     FOR(i,1,n-1) {
67         set<PIN>::iterator it = s.upper_bound(MP(a[i],(Node*)NULL));
68         if(it!=s.end() and it->second->son[0]==NULL) {
69             (it->second)->son[0] = new Node(a[i]);
70             s.insert(MP(a[i], it->second->son[0]));
71             ans[i] = it->second->value;
72         } else {
73             set<PIN>::iterator it2 = it;
74             if(it2!=s.begin())it2--;
75             it2->second->son[1] = new Node(a[i]);
76             s.insert(MP(a[i], it2->second->son[1]));
77             ans[i] = it2->second->value;
78         }
79 
80     }
81 }
82 
83 
84 int main() {
85     int i;
86     RD(n);
87     REP(i,n)RD(a[i]);
88     farm();
89     OA(ans,1,n-1);
90     return 0;
91 }
View Code

E. Trains and Statistic

題意:有一個一條直線的地鐵線路。給出a陣列,每個站點i只能買到去往[i+1, a[i]]內的票。設p(i,j)為從i到j所需要的最少票數,求對所有ij的p(i,j)的和。(1=<i<j<=n)

題解:

設f[x]為從站點x到它之後所有站點票數的和。

簡單設想,f[x]的值對f[x-1] f[x-2]等等各個值的計算是有用的。

當從一個站點i到不了所有點時,會到它能到的點中a[i]最大的點x。這時就能用到f[x]。

b[i] = x-i + b[x] + n - a[i]

其中自己能走i+1~x-1點,用x-i票。

x能到x+1~n,用b[x]票。

x能走的那些中,x+1 ~ a[i]是i自己能走的,把x走的當做自己走的,更遠的要自己買票走到x,要n - a[i]張票。

綜合起來就是上面那個公式。

x能走的肯定比a[i]遠,因為a[a[i]]肯定要大於a[i]。

這樣,我們要做的就是每次找出區間[i+1, a[i]]中a[x]最大的x。

這可以用各種RMQ方法。不能用單調區間O(1)求,因為這個區間不是純粹向左移動的,左界是一個個往左,右界是會來回動的。

所以我們可以維護一個只進不出的單調下降佇列,然後用二分找。

O(nlogn)

 1 from collections import deque
 2 
 3 def argmax(q,z):
 4     l = 0
 5     r = len(q) - 1
 6     while(l<=r):
 7         mid = (l+r)/2
 8         x = q[mid]['i']
 9         if(x<=z):
10             r = mid - 1
11         else:
12             l = mid + 1
13     return q[l]['i']
14 
15 def gank(n,A):
16     a = [0]*(n+1)
17     a[1:] = A
18     b = [0]*(n+1)
19     b[n-1] = 1
20     q = deque()
21     q.append({'i':n-1, 'a':a[n-1]})
22     for i in range(n-2, 0, -1):
23         if(a[i]>=n):
24             b[i] = n-i
25         else:
26             x = argmax(q,a[i])
27             b[i] = x-i + b[x] + n - a[i]
28         while(len(q)>0 and q[-1]['a'] < a[i]):
29             q.pop()
30         q.append({'i':i, 'a':a[i]})
31     return sum(b)
32 
33 n = int(raw_input())
34 a = map(int , raw_input().split(' '))
35 print gank(n,a)
View Code