Andrew Stankevich's Contest #1
Solution
Problem A: ChineseGirls' Amusement
Problem B: ReactorCooling
Problem C: NewYear Bonus Grant
Problem D: MatrixMultiplication
Problem E: NicePatterns Strike Back
Problem F: Get Out!
Problem G: BeautifulPeople
Problem H: Cracking'RSA
July 18th,2013 by chlxyd,xioumu,Eyelids
Problem A: ChineseGirls' Amusement(E)
求最大的k<=n/2使得gcd(n,k)=1。
Solution
Tag:数论
第一部分:
首先证明要满足gcd(n, m) = 1。
{
将n个人的排列重复循环下去,如n = 3时形成1 2 3 1 2 3 1 2 3 ……的数列
球每传n次显然是一个循环,传完n次后停在数列的第n * m+ 1项上。
因为球停在该数列第lcm(n,m) + 1个数的位置上时,才第一次回到第一个人手上
那么若要满足在前n次传球过程中不重复经过一人之手,显然可以得到n * m + 1 = lcm(n, m) + 1
即 lcm(n,m) = n * m,因为gcd(n,k)*gcd(n,k)=n*k,故有gcd(n, m) = 1
}
第二部分:
题目要求m <= n / 2,且m最大
当n = 2k + 1时,可证m = k即满足条件。
因为gcd(n, m) = gcd(n - m, m),所以gcd(2k + 1, k) = gcd(k + 1, k) = 1。
当n = 2k时,
1.若m = k, gcd(2k, k)显然不为1
2.若m = k - 1, gcd(2k, k - 1) = gcd(k - 1, k +1) = gcd(k + 1, 2),显然当k为奇数时gcd不为1
故k为偶数时,m = k - 1
而k为奇数时,取m = k - 2。gcd(2k, k - 2) = gcd(k + 2, k - 2) =gcd(4, k + 2),已经满足条件。
By Eyelids
import java.io.*;
import java.math.*;
import java.util.*;
public class Main {
public final static BigInteger ZERO = new BigInteger("0");
public final static BigInteger ONE = new BigInteger("1");
public final static BigInteger TWO = new BigInteger("2");
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int T;
BigInteger n, m, ans;
T = cin.nextInt();
for (int t = 0; t < T; t++) {
if (t != 0)
System.out.println();
n = cin.nextBigInteger();
m = n.divide(TWO);
while (true) {
//System.out.println(m.gcd(n));
if (m.gcd(n).compareTo(ONE) == 0) {
ans = m;
break;
}
m = m.subtract(ONE);
}
System.out.println(ans);
}
}
}
=====================================================
Problem B: ReactorCooling(N)
给一个网络管道图,每条边存在流量上限和下限,问是否存在一种方案使所有顶点的出度和入度相同。
Solution
Tag:网络流
经典的无源无汇上下界网络流模型,首先假设所有顶点均满流,然后一定存在某些顶点有正盈余,有些顶点有负盈余,引入一个源点和汇点,源点和所有正盈余的点连一条容量为盈余量那么多的边,所有负盈余的点和汇点连一条容量为盈余量那么多的边,原图中所有边的容量为其上下界之差,在这个图中跑最大流,如果存在的话,该流就是原流的一个调整流(把所有不合法的流量调整),用原流的各个边流量减去当前的各个边的流量,就是我们合法的流的流量。如果构造出的图最大流不等于源点流出的流量的话,表示没有办法把这些负盈余调整为0,原图无解。
/*
* Author: chlxyd
* Created Time: 2013/7/17 16:12:56
* File Name: B.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define clr(x) memset( x , 0 , sizeof(x) )
#define sz(v) ((int)(v).size())
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define repd(i, a, b) for (int i = (a); i >= (b); --i)
#define clrs( x , y ) memset( x , y , sizeof(x) )
#define maxn 400
#define maxm 100000
#define inf 1000000
int e[maxn][maxn] ;
int n , m , ans , sum ;
queue<int> q ;
int d[maxn];
bool bj[maxn] ;
int val[maxn] ;
int a[maxm] , b[maxm] , c[maxm] , dd[maxm] ;
int T ;
int dfs( int t , int v ) {
if ( t == n || v == 0 ) return v ;
int ans = 0 , flow ;
repf( i , 1 , n ) {
if ( e[t][i] > 0 && d[i] == d[t] + 1 ) {
flow = dfs( i , min( v , e[t][i] ) ) ;
e[t][i] -= flow ; e[i][t] += flow ;
v -= flow ; ans += flow ;
if ( v == 0 ) break ;
}
}
return ans ;
}
bool bfs( int s ) {
int x ;
repf( i , 1 , n ) {
bj[i] = 0 ;
}
while (!q.empty() ) q.pop() ;
q.push(s) ; d[s] = 1 ; bj[s] = true ;
while ( !q.empty() ) {
int x = q.front() ; q.pop() ;
repf( i , 1 , n ) {
if ( !bj[i] && e[x][i] > 0 ) {
bj[i] = true ;
d[i] = d[x] + 1 ;
q.push(i) ;
}
}
}
return bj[n] ;
}
void clrr() {
ans = 0 ;
repf( i , 1 , n + 2 )
repf( j , 1 , n + 2 )
e[i][j] = 0 ;
repf( i , 1 , n + 2 ) val[i] = 0 ;
sum = 0 ;
}
int main(){
scanf("%d" , &T ) ;
repf( t , 1 , T ) {
if ( t != 1 ) puts("") ;
scanf("%d %d" , &n , &m ) ;
clrr() ;
repf( i , 1 , m ) {
scanf("%d %d %d %d" , &a[i] , &b[i] , &c[i] , &dd[i] ) ;
a[i] ++ ; b[i] ++ ;
val[a[i]] -= dd[i] ; val[b[i]] += dd[i] ;
e[a[i]][b[i]] = dd[i] - c[i] ;
}
repf( i , 2 , n + 1 ) {
if ( val[i] < 0 ) {
e[1][i] = - val[i] ;
sum += e[1][i] ;
}
if ( val[i] > 0 ) e[i][n+2] = val[i] ;
}
n += 2 ;
//repf( i , 1 , n ) {
//repf( j , 1 , n )
//printf("%d " , e[i][j] ) ;
//cout<<endl;
//}
while ( bfs(1) )
ans += dfs( 1 , inf ) ;
//cout<<endl;
//repf( i , 1 , n ) {
//repf( j , 1 , n )
//printf("%d " , e[i][j] ) ;
//cout<<endl;
//}
if ( ans == sum ) {
puts("YES") ;
repf( i , 1 , m )
printf("%d\n" , dd[i] - e[b[i]][a[i]] ) ;
}
else puts("NO") ;
}
}
=====================================================
Problem C: NewYear Bonus Grant(E)
一棵树,每个节点要么收到父节点的一份礼物,要么给其一个儿子节点一份礼物,要么什么都不做,问最多需要多少份礼物。
Solution
Tag:DP
Dp[i][0]表示第i个点为根的子树,i点没有给儿子礼物的最大代价,dp[i][1]表示i点为根的子树,i点给了某个儿子礼物的最大代价。那么dp[i][0]显然从max(dp[k][0],dp[k][1])(k是i儿子)转移过来,dp[i][1]也从max(dp[k][0],dp[k][1])转移过来,但至少需要1个dp[k][0]。
/*
* Author: chlxyd
* Created Time: 2013/7/17 14:46:36
* File Name: C.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define clr(x) memset( x , 0 , sizeof(x) )
#define sz(v) ((int)(v).size())
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define repd(i, a, b) for (int i = (a); i >= (b); --i)
#define clrs( x , y ) memset( x , y , sizeof(x) )
vector<int> v[510000] , ret ;
int f[510000][2] ;
int g[510000] ;
void dfs( int i , int fa ) {
if ( v[i].size() == 1 && fa != 0 ) {
f[i][0] = f[i][1] = 0 ;
return ;
}
bool flag = false ;
int now = 1000000000 ;
rep( j , v[i].size() ) {
int k = v[i][j] ;
if ( k == fa ) continue ;
dfs(k,i) ;
f[i][0] += max( f[k][0] , f[k][1] ) ;
if ( f[k][0] >= f[k][1] ) {
g[i] = k ;
flag = true ;
f[i][1] += f[k][0] ;
}
else {
if ( !flag && now > f[k][1] - f[k][0] ) {
now = min( now , f[k][1] - f[k][0] ) ;
g[i] = k ;
}
f[i][1] += f[k][1] ;
}
}
if ( !flag )
f[i][1] -= now ;
f[i][1] ++ ;
}
void getway( int i , int val , int fa ) {
if ( v[i].size() == 1 && fa != 0 ) return ;
if ( val == 0 ) {
rep( j , v[i].size() ) {
int k = v[i][j] ;
if ( k == fa ) continue ;
getway(k,f[k][0]>f[k][1]?0:1,i) ;
}
}
else if ( val == 1 ) {
getway( g[i] , 0 , i ) ;
ret.push_back(g[i]) ;
rep( j , v[i].size() ) {
int k = v[i][j] ;
if ( k == fa || k == g[i] ) continue ;
getway(k,f[k][0]>f[k][1]?0:1,i) ;
}
}
}
int main(){
int T ;
scanf("%d" , &T ) ;
repf( t , 1 , T ) {
if ( t != 1 ) printf("\n" ) ;
int n ;
scanf("%d" , &n ) ;
repf( i , 1 , n ) {
v[i].clear() ;
f[i][1] = f[i][0] = 0 ;
g[i] = 0 ;
}
ret.clear() ;
repf( i , 2 , n ) {
int a ;
scanf("%d" , &a ) ;
v[i].push_back(a) ;
v[a].push_back(i) ;
}
dfs(1,0) ;
getway(1,f[1][0]>f[1][1]?0:1,0) ;
printf("%d\n" , 1000*max(f[1][0],f[1][1]));
sort(ret.begin(),ret.end());
rep( j , ret.size() ) {
if ( j != 0 ) printf(" ") ;
printf("%d" , ret[j]) ;
}
puts("") ;
}
}
=====================================================
Problem D: MatrixMultiplication(E)
给出了一个和图有关的矩阵A,求ATA的值。
Solution
Tag:数学
自己画几个矩阵试试就知道答案了,分别计算每个顶点出现的次数,然后求平方和。
/*
* Author: chlxyd
* Created Time: 2013/7/17 13:01:23
* File Name: D.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define clr(x) memset( x , 0 , sizeof(x) )
#define sz(v) ((int)(v).size())
#define rep(i, n) for (long long i = 0; i < (n); ++i)
#define repf(i, a, b) for (long long i = (a); i <= (b); ++i)
#define repd(i, a, b) for (long long i = (a); i >= (b); --i)
#define clrs( x , y ) memset( x , y , sizeof(x) )
long long n , m ;
long long T ;
long long f[11000] ;
int main(){
scanf("%lld" , &T ) ;
while ( T -- ) {
scanf("%lld %lld" , &n , &m ) ;
repf( i , 1 , n ) f[i] = 0 ;
repf( i , 1 , m ) {
long long a , b ;
scanf("%lld %lld" , &a , &b ) ;
f[a] ++ ; f[b] ++ ;
}
long long ans = 0 ;
repf( i , 1 , n ) ans += f[i] * f[i] ;
printf("%lld\n" , ans ) ;
if ( T != 0 )
printf("\n" ) ;
}
//puts("sdfs");
}
=====================================================
Problem E: NicePatterns Strike Back(N)
给个n*m的网格,每个格子可以涂黑或者图白,要求涂完后网格里不能有2*2的全白或者全黑的网格区域。
问有多少种不同的填法。
N <= 10^100, m<= 5
Solution
Tag:状态压缩dp, 矩阵乘法
可以发现每一行状态合不合法都只跟上一行有关,而M只有5,所以压缩行的状态,可以先用暴力求出每一个状态可以连接哪些下一行的状态,做出矩阵. 然后用矩阵乘法求快速幂。
By xioumu
/*
* Author: chlxyd
* Created Time: 2013/7/17 13:55:55
* File Name: E.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define clr(x) memset( x , 0 , sizeof(x) )
#define sz(v) ((int)(v).size())
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define repd(i, a, b) for (int i = (a); i >= (b); --i)
#define clrs( x , y ) memset( x , y , sizeof(x) )
const int maxn = 42;
int mod;
struct matrix{
int ar[maxn][maxn];
int n, m;
matrix(int _n = 0, int _m = 0) :n(_n), m(_m) {
}
void clear() {
rep (i, n)
rep (j, m)
ar[i][j] = 0;
}
void setOne() {
clear();
rep (i, min(n, m)) {
ar[i][i] = 1 % mod;
}
}
void output() {
printf("%d %d\n", n, m);
rep (i, n) {
rep (j, m)
printf("%d ", ar[i][j]);
puts("");
}
puts("");
}
};
matrix operator * (const matrix &a, const matrix &b) {
matrix c;
if (a.m != b.n) printf("a.m != b.n\n");
c.n = a.n;
c.m = b.m;
c.clear();
rep (i, a.n)
rep (j, b.m)
rep (k, a.m) {
c.ar[i][j] += a.ar[i][k] * b.ar[k][j];
c.ar[i][j] %= mod;
}
return c;
}
vector<int> n;
int m;
char ch[10100];
int two(int w) {
return 1 << w;
}
bool can(int a, int b) {
int t[10][10];
memset(t, 0, sizeof(t));
rep (i, m) {
if ((two(i) & a) != 0) {
t[0][i] = 1;
}
}
rep (i, m) {
if ((two(i) & b) != 0) {
t[1][i] = 1;
}
}
rep (i, m) {
if (i != 0) {
if (t[0][i - 1] && t[1][i - 1] && t[0][i] && t[1][i])
return false;
if (!t[0][i - 1] && !t[1][i - 1] && !t[0][i] && !t[1][i])
return false;
}
}
return true;
}
matrix getB(int m) {
matrix res;
res = matrix(two(m), two(m));
res.clear();
rep (i, two(m))
rep (j, two(m)) {
if (can(i, j)) {
res.ar[i][j] = 1;
}
else res.ar[i][j] = 0;
}
return res;
}
void div(vector<int> &x) {
repd (i, sz(n) - 1, 0) {
int a = x[i] / 2;
int b = x[i] % 2;
x[i] = a;
if (i != 0)
x[i - 1] += b * 10;
}
repd (i, sz(n) - 1, 0) {
if (x[i] == 0)
x.pop_back();
else break;
}
}
void dec(vector<int> &n) {
n[0]--;
rep (i, sz(n)) {
if (n[i] < 0) {
n[i + 1]--;
n[i] += 10;
}
else break;
}
repd (i, sz(n) - 1, 0) {
if (n[i] == 0) {
n.pop_back();
}
else break;
}
}
matrix mypow(matrix b, vector<int> &n) {
matrix res = matrix(two(m), two(m));
res.setOne();
while (sz(n) != 0) {
if (n[0] % 2 != 0) {
res = res * b;
}
b = b * b;
div(n);
//repd (i, sz(n) - 1, 0)
//printf("%d", n[i]);
//printf("\nsz:%d\n", sz(n));
}
return res;
}
int gao(matrix b, int m) {
matrix fr = matrix(two(m), 1), res;
rep (i, two(m)) {
fr.ar[i][0] = 1;
}
dec(n);
res = mypow(b, n);
//res.output();
//fr.output();
res = res * fr;
//res.output();
int ans = 0;
rep (i, two(m)) {
ans += res.ar[i][0];
ans %= mod;
}
return ans;
}
int main() {
int T;
scanf("%d", &T);
rep (t, T) {
if (t != 0) printf("\n");
scanf("%s", ch);
int len = strlen(ch);
n.clear();
rep (i, len)
n.push_back(ch[i] - '0');
reverse(n.begin(), n.end());
scanf("%d%d", &m, &mod);
matrix b = getB(m);
//b.output();
//puts("test");
int ans = gao(b, m);
printf("%d\n", ans);
}
return 0;
}
=====================================================
Problem F: Get Out!(V)
你是一个圆,周围还有很多圆,问你是否被圆围住了。
Solution
Tag:计算几何
先把所有其他圆的半径加上自己的半径,这样问题就转化成了,你是点A,是否被这些圆给包围了。
也就是变成了将被有重合的圆的圆心连起来,判断是否围住了这个点A。
然后可以转化成图论。
将每个圆看成一个节点,若两个圆相连,则把这两节点连一条有向边,边权是这条边与点A的有向夹角。
最后只要判断这个图存不存在负环即可。
原理与判断判断点在多边形内的 角度和判别法一样。
By xioumu
/*
* Author: xioumu
* Created Time: 2013/7/17 17:22:30
* File Name: f.cpp
* solve: f.cpp
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#define sz(v) ((int)(v).size())
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define repd(i, a, b) for (int i = (a); i >= (b); --i)
#define clr(x) memset(x,0,sizeof(x))
#define clrs( x , y ) memset(x,y,sizeof(x))
#define out(x) printf(#x" %d\n", x)
#define sqr(x) ((x) * (x))
typedef long long lint;
const double pi = acos(-1.0);
const int maxint = -1u>>1;
const double eps = 1e-6;
const double eps2 = 1e-6;
int sgn(const double &x) { return (x > eps) - (x < -eps); }
const int maxn = 600 + 10;
struct point {
double x, y;
point (double _x = 0, double _y = 0) : x(_x), y(_y) {
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.3f %.3f\n", x, y);
}
double len() const {
return sqrt(x * x + y * y);
}
point trunc (double l) const {
double r = l / len();
return point(x * r, y * r);
}
point rotate_left() const {
return point(-y, x);
}
point rotate_right() const {
return point(y, -x);
}
point rotate_left(double s) const {
double c = sqrt(1 - s * s);
return point(x * c - y * s, y * c + x * s);
}
point rotate_right(double s) const {
double c = sqrt(1 - s * s);
return point(x * c + y * s, y * c - x * s);
}
point set() {
double l = len();
return point(x / l, y / l);
}
bool operator == (const point &p) const {
return sgn(x - p.x) == 0 && sgn(y - p.y) == 0;
}
double operator * (const point &p) const {
return (x * p.y) - (y * p.x);
}
double operator ^ (const point &p) const {
return (x * p.x) + (y * p.y);
}
point operator + (const point &p) const {
return point(x + p.x, y + p.y);
}
point operator - (const point &p) const {
return point(x - p.x, y - p.y);
}
point operator / (double mul) const {
return point(x / mul, y / mul);
}
bool operator < (const point &p) const{
if(sgn(x - p.x) != 0) return x < p.x;
else return y < p.y;
}
};
double trim(double d, double l = 1.0) {
return d > l ? l : (d < -l ? -l : d);
}
struct circle {
point s;
double r;
void input() {
s.input();
scanf("%lf", &r);
}
};
vector<int> e[maxn];
vector<double> d[maxn];
vector<circle> island;
circle me;
int n;
void add(int x, int y, double z) {
e[x].push_back(y);
d[x].push_back(z);
}
bool touch(circle a, circle b) {
if ((a.s - b.s).len() - a.r - b.r >= -eps2) return false;
else return true;
}
void getTu() {
rep (i, n) {
e[i].clear();
d[i].clear();
}
rep (i, n) {
rep (j, n) {
if (touch(island[i], island[j])) {
point p1 = island[i].s - me.s, p2 = island[j].s - me.s;
double c = (p1 ^ p2) / (p1.len() * p2.len());
c = trim(c);
add(i, j, sgn(p1 * p2) * acos(c));
//printf("==>%d %d\n", i, j);
}
}
}
}
queue<int> que;
double dis[maxn];
int v[maxn], times[maxn];
int check() {
rep (i, n) {
dis[i] = 0;
v[i] = times[i] = 0;
que.push(i);
}
while (!que.empty()) {
int k = que.front();
que.pop();
v[k] = 0;
rep (i, sz(e[k])) {
int j = e[k][i];
if (sgn(dis[j] - (dis[k] + d[k][i])) > 0){
dis[j] = dis[k] + d[k][i];
if (v[j] == 0) {
v[j] = 1;
que.push(j);
times[j]++;
if (times[j] > n) return true;
}
}
}
}
return false;
}
int main() {
int T;
scanf("%d", &T);
rep (t, T) {
if (t != 0) printf("\n");
scanf("%d", &n);
island.clear();
rep (i, n) {
circle x;
x.input();
island.push_back(x);
}
me.input();
rep (i, n) {
island[i].r += me.r;
}
getTu();
//printf("%d\n", sz(more));
int ans = check();
if (ans == 1) printf("NO\n");
else printf("YES\n");
}
return 0;
}
=====================================================
Problem G: BeautifulPeople(N)
选取最多的人,要求不存在两个人i和j,Si <= Sj&& Bi >= Bj。
Solution
Tag:DP
先安第一关键字升序排序,相同的安第二关键字降序排序,然后用o(nlogn)的方法求最长上升子序列的长度和方案,就是所求的答案。
By xioumu
/*
* Author: chlxyd
* Created Time: 2013/7/17 15:20:08
* File Name: G.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define clr(x) memset( x , 0 , sizeof(x) )
#define sz(v) ((int)(v).size())
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define repd(i, a, b) for (int i = (a); i >= (b); --i)
#define clrs( x , y ) memset( x , y , sizeof(x) )
const int maxn = 100000 + 10;
struct node {
int x, y;
node (int _x = 0, int _y = 0) : x(_x), y(_y){
}
};
vector<node> a;
int f[maxn], b[maxn], c[maxn], id[maxn], p[maxn];
int ans[maxn];
int n, m;
bool cmp(const int &r, const int &w) {
if (a[r].x != a[w].x) return a[r].x < a[w].x;
else return a[r].y > a[w].y;
}
bool cmpb(const int &r, const int &w) {
return r < w;
}
int find(int w) {
if (m == -1) return 0;
int h = lower_bound(b, b + m + 1, w, cmpb) - b;
return h;
}
void putAns() {
int now = id[m];
int i = 0;
while (now != -1) {
ans[i++] = now;
now = p[now];
}
repd (i, m, 0) {
if (i != m) printf(" ");
printf("%d", ans[i] + 1);
}
puts("");
}
int main(){
int T;
scanf("%d", &T);
rep (t, T) {
if (t != 0) printf("\n");
scanf("%d", &n);
a.clear();
rep (i, n) {
int x, y;
scanf("%d%d", &x, &y);
a.push_back(node(x, y));
c[i] = i;
}
sort(c, c + n, cmp);
//rep (i, n) {
//printf("%d %d\n", a[c[i]].x, a[c[i]].y);
//}
m = -1;
memset(p, -1, sizeof(p));
rep (i, n) {
int h = find(a[c[i]].y);
//printf(" %d : %d %d %d %d\n", i, h, a[c[i]].y, b[h], m);
if (h > m) {
m++;
b[m] = a[c[i]].y;
id[m] = c[i];
if (m != 0)
p[id[m]] = id[m - 1];
}
else if (a[c[i]].y < b[h]) {
b[h] = a[c[i]].y;
id[h] = c[i];
if (h != 0)
p[id[h]] = id[h - 1];
}
}
printf("%d\n", m + 1);
putAns();
}
return 0;
}
=====================================================
Problem H: Cracking'RSA(H)
有100个数,每个数的因子是前100个质数,问有多少集合使集合里面数的积是完全平方数。
Solution
Tag:高斯消元
对于每一个因子,a[i][j]=1表示第i个数含有奇数个,否则表示含有偶数个,x[i]表示第i个因数是否选,于是对于每一个因子有方程:
a[1][j]*x[1]^(异或)a[2][j]*x[2]^…^a[n][j]*x[n]= 0
于是所有因子构成一个异或方程组,用高斯消元解这个方程组,然后求出变元个数r,答案为2^r-1。
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define clr(x) memset( x , 0 , sizeof(x) )
#define sz(v) ((int)(v).size())
#define rep(i, n) for (long long i = 0; i < (n); ++i)
#define repf(i, a, b) for (long long i = (a); i <= (b); ++i)
#define repd(i, a, b) for (long long i = (a); i >= (b); --i)
#define clrs( x , y ) memset( x , y , sizeof(x) )
const int MAXN=150;
int a[MAXN][MAXN];//增广矩阵
int x[MAXN];//解集
bool free_x[MAXN];//标记是否是不确定的变元
int n , m , T ;
void Debug( int equ , int var )
{
int i, j;
for (i = 0; i < equ; i++)
{
for (j = 0; j < var + 1; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
inline int gcd(int a,int b){
int t;
while(b!=0){
t=b;
b=a%b;
a=t;
}
return a;
}
inline int lcm(int a,int b){
return a/gcd(a,b)*b;//先除后乘防溢出
}
// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,
//-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
//有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var.
int Gauss(int equ,int var){
int i,j,k;
int max_r;// 当前这列绝对值最大的行.
int col;//当前处理的列
int ta,tb;
int LCM;
int temp;
int free_x_num;
int free_index;
for(int i=0;i<=var;i++){
x[i]=0;
free_x[i]=true;
}
//转换为阶梯阵.
col=0; // 当前处理的列
for(k = 0;k < equ && col < var;k++,col++){// 枚举当前处理的行.
// 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
max_r=k;
for(i=k+1;i<equ;i++){
if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
}
if(max_r!=k){// 与第k行交换.
for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
}
if(a[k][col]==0){// 说明该col列第k行以下全是0了,则处理当前行的下一列.
k--;
continue;
}
for(i=k+1;i<equ;i++){// 枚举要删去的行.
if(a[i][col]!=0){
for(j=col;j<var+1;j++){
a[i][j] = a[i][j]^a[k][j];
}
}
}
}
// Debug();
// 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
for (i = k; i < equ; i++){ // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换.
if (a[i][col] != 0) return -1;
}
// 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.
// 且出现的行数即为自由变元的个数.
if (k < var){
// 首先,自由变元有var - k个,即不确定的变元至少有var - k个.
for (i = k - 1; i >= 0; i--){
// 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行.
// 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的.
free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元.
for (j = 0; j < var; j++){
if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
}
if (free_x_num > 1) continue; // 无法求解出确定的变元.
// 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的.
temp = a[i][var];
for (j = 0; j < var; j++){
if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j];
}
x[free_index] = temp / a[i][free_index]; // 求出该变元.
free_x[free_index] = 0; // 该变元是确定的.
}
return var - k; // 自由变元有var - k个.
}
// 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
// 计算出Xn-1, Xn-2 ... X0.
for (i = var - 1; i >= 0; i--){
temp = a[i][var];
for (j = i + 1; j < var; j++){
if (a[i][j] != 0) temp -= a[i][j] * x[j];
}
if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解.
x[i] = temp / a[i][i];
}
return 0;
}
int jl[1100] ;
bool bj[1100] ;
void getp() {
int top = -1 ;
for ( int i = 2 ; i <= 1000 ; i ++ ){
if ( !bj[i] ) jl[++top] = i ;
for ( int j = i ; j <= 1000 ; j += i )
bj[j] = true ;
}
}
struct big{
int a[5000] ;
int l ;
void clear(){
clr(a);l=1;
}
void minu() {
a[1] -- ;
repf( i , 1 , l ) {
if ( a[i] < 0 ) {
a[i+1] -- ;
a[i] = a[i] + 10 ;
}
}
if ( a[l] == 0 ) l -- ;
}
big operator + ( const big &q ) const {
big now ;
now.clear();
now.l = max( l , q.l ) ;
repf( i , 1 , now.l )
now.a[i] = a[i] + q.a[i] ;
repf( i , 1 , now.l + 2 ) {
if ( now.a[i] >= 10 ){
now.a[i+1] += now.a[i] / 10 ;
now.a[i] %= 10 ;
}
}
if ( now.a[now.l+1] != 0 ) now.l ++ ;
return now ;
}
big operator * ( const int &q ) const {
big now ; now.clear() ;
int dl = 0 , tmp = q ;
while ( tmp ) {
dl ++ ;
tmp /= 10 ;
}
now.l = l + dl;
repf( i , 1 , l )
now.a[i] += a[i] * q ;
repf( i , 1 , now.l ) {
if ( now.a[i] >= 10 ){
now.a[i+1] += now.a[i] / 10 ;
now.a[i] %= 10 ;
}
}
if ( now.a[now.l] == 0 ) now.l -- ;
return now ;
}
big operator * ( const big &q ) const {
big now ; now.clear() ;
now.l = l + q.l ;
repf( i , 1 , l )
repf( j , 1 , q.l )
now.a[i+j-1] += a[i] * q.a[j] ;
repf( i , 1 , now.l ) {
if ( now.a[i] >= 10 ){
now.a[i+1] += now.a[i] / 10 ;
now.a[i] %= 10 ;
}
}
if ( now.a[now.l] == 0 ) now.l -- ;
return now ;
}
big operator / ( const int &q ) const{
int now = 0 ;
big ret ; ret.clear() ;
repd( i , l , 1 ) {
now = now * 10 + a[i] ;
if ( now < q ) continue ;
ret.a[i] = now / q ;
now = now % q ;
}
repd( i , l , 1 )
if ( ret.a[i] != 0 ) {
ret.l = i ;
break ;
}
return ret ;
}
void show( ) {
while ( l > 1 && a[l] == 0 ) l -- ;
repd( i , l , 1 )
printf("%d" , a[i] ) ;
printf("\n") ;
}
};
big power(big a,int n){
big ret; ret.clear() ; ret.a[1] = 1 ;
for(;n;n>>=1){
if(n & 1) ret=ret*a;
a=a*a;
}
return ret;
}
int main(void){
//freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
getp() ;
scanf("%d" , &T ) ;
repf( t , 1 , T ) {
if ( t != 1 )
puts("") ;
scanf("%d %d" , &n , &m ) ;
repf( i , 0 , m - 1 ) {
int now ;
scanf("%d" , &now ) ;
repf( j , 0 , n - 1 ) {
int num = 0 ;
while ( now % jl[j] == 0 ) {
num ^= 1 ;
now /= jl[j] ;
}
a[j][i] = num ;
}
}
rep( i , n ) a[i][m] = 0 ;
//Debug(n,m) ;
int free_num = Gauss(n,m) ;
if ( free_num <= 0 ) puts("0") ;
else {
big now ; now.clear() ;now.a[1] = 2 ;
now = power( now , free_num ) ;
now.minu() ;
now.show() ;
}
}
}
=====================================================