Codeforces Round #285 (Div. 2) ABCD
A题
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 7 using namespace std; 8 9 #define LL long long 10 #define eps 1e-8 11 #define inf 0x3f3f3f3f 12 #define mnx 100010 13 14 int main(){ 15 int a, b, c, d; 16 scanf( "%d%d%d%d", &a, &b, &c, &d ); 17 int a1 = max( 3 * a / 10, a - a / 250 * c ); 18 int a2 = max( 3 * b / 10, b - b / 250 * d ); 19 if( a1 > a2 ) puts( "Misha" ); 20 else if( a1 == a2 ) puts( "Tie" ); 21 else puts( "Vasya" ); 22 return 0; 23 }
B题
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 7 using namespace std; 8 9 #define LL long long 10 #define eps 1e-8 11 #define inf 0x3f3f3f3f 12 #define mnx 1010 13 14 char s[mnx][mnx], ch[mnx][mnx]; 15 int main(){ 16 int n, m = 0; 17 scanf( "%d", &n ); 18 for( int i = 0; i < n; ++i ){ 19 cin >> s[m] >> ch[m]; 20 bool ok = 1; 21 int j; 22 for( j = 0; j < m; ++j ){ 23 if( strcmp( ch[j], s[m] ) == 0 ){ ok = 0; break; } 24 } 25 if( ok ) m++; 26 else strcpy( ch[j], ch[m] ); 27 } 28 cout << m << endl; 29 for( int i = 0; i < m; ++i ){ 30 cout << s[i] << " " << ch[i] << endl; 31 } 32 }
C题 傻逼了,想错了。。后来发现把相连的边的数量当做入度,把全部node都塞进优先队列里,每次弹出度数最小的点u。如果访问过或者度数为0,就continue;否则就找到 v = u.val ^ x[u],给v所在节点的入度减1,x[v] ^= u,再把v塞进队列里,同时标记u已经访问过。记录边(u,v)我直接就用了vector
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <queue> 7 #include <vector> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-8 13 #define inf 0x3f3f3f3f 14 #define mnx 300100 15 16 vector<int> g[mnx]; 17 struct node{ 18 int in, val, id; 19 bool operator < ( const node &b ) const { 20 return in > b.in; 21 } 22 }c[mnx]; 23 int x[mnx]; 24 bool vis[mnx]; 25 int main(){ 26 int n, m = 0; 27 scanf( "%d", &n ); 28 priority_queue<node> q; 29 for( int i = 0; i < n; ++i ){ 30 scanf( "%d %d", &c[i].in, &c[i].val ); 31 c[i].id = i; 32 q.push( c[i] ); 33 } 34 while( !q.empty() ){ 35 node kk = q.top(); q.pop(); 36 int u = kk.id; 37 if( vis[u] ) continue; 38 vis[u] = 1; 39 if( kk.in == 0 ) continue; 40 int v = kk.val ^ x[u]; 41 x[v] ^= u; 42 c[v].in--; 43 q.push( c[v] ); 44 g[u].push_back( v ); 45 m++; 46 } 47 cout << m << endl; 48 for( int i = 0; i < n; ++i ){ 49 int k = g[i].size(); 50 for( int j = 0; j < k; ++j ) 51 cout << i << " " << g[i][j] << endl; 52 } 53 return 0; 54 }
D题 看到别人的题解说的 康托展开。果然知识面不够丰富。
如我想知道321是{1,2,3}中第几个小的数可以这样考虑 :
第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个小的数。 2*2!+1*1!+0*0!就是康托展开。
一个全排列 ,可以看成a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!, 那么其实只要处理出每一项的系数,然后把两个序列的系数加起来,然后进行进位操作,就可以求出答案的每一项的系数。求系数实际上就是求后面有多少个数是小于这个数的。这个过程要利用树状数组来维护。
求出答案的系数后,再二分找答案(开始的时候一直都写不对这个二分,后来看了题解的二分,发现自己有智商捉急了)。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <queue> 7 #include <vector> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-8 13 #define inf 0x3f3f3f3f 14 #define mnx 300100 15 16 int p[mnx], q[mnx], bit[mnx], n; 17 int sum( int x ){ 18 int ret = 0; 19 while( x > 0 ){ 20 ret += bit[x]; 21 x -= x & -x; 22 } 23 return ret; 24 } 25 void add( int i, int x ){ 26 while( i <= n ){ 27 bit[i] += x; 28 i += i & -i; 29 } 30 } 31 int main(){ 32 scanf( "%d", &n ); 33 for( int i = 1; i <= n; ++i ){ 34 scanf( "%d", &p[i] ); 35 int tmp = sum( p[i] ); 36 q[i] += p[i] - tmp; 37 add( p[i] + 1, 1 ); 38 } 39 memset( bit, 0, sizeof( bit ) ); 40 for( int i = 1; i <= n; ++i ){ 41 scanf( "%d", &p[i] ); 42 int tmp = sum( p[i] ); 43 q[i] += p[i] - tmp; 44 add( p[i] + 1, 1 ); 45 } 46 for( int i = n; i > 0; --i ) 47 if( q[i] >= ( n - i + 1) ){ 48 q[i-1] += q[i] / ( n - i + 1 ); 49 q[i] %= ( n - i + 1); 50 } 51 memset( bit, 0, sizeof( bit ) ); 52 for( int i = 1; i <= n; ++i ) add( i, 1 ); 53 for( int i = 1; i <= n; ++i ){ 54 int l = 1, r = n; 55 while( l < r ){ 56 int mid = ( l+r ) >> 1; 设q[i] = 2, mid = 4, 数字2已经用了。 57 int tmp = sum( mid ); 假设4还没有用,这时tmp = 3, tmp - 1 == 2 == q[i], 4应该是右端点 58 if( tmp - 1 < q[i] ) 假设4已经用了,这时tmp = 2, tmp - 1 == 1 < q[i], 此时5应该是左端点 59 l = mid + 1; 60 else r = mid; 61 } 62 cout << l - 1 << " "; 63 add( l, -1 ); 64 } 65 return 0; 66 }