Andrew Stankevich's Contest #1 Solution


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

TagDP

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

TagDP

先安第一关键字升序排序,相同的安第二关键字降序排序,然后用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() ;
        }
    }
}


=====================================================

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值