SCAU 2015 GDCPC team_training1

A: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1525

题意:前两段都是废话 , 然后给出一个 p( p(a) ) = p(a) 的公式给你 , 符合这个公式的1~n序列属于idempotent , 题目就问有多少个是 non - idempotent 的

对于这个公式 , 可以知道要符合的话,1~n要按顺序摆放 。 那么答案就是 n ! - 1 了

#include <bits/stdc++.h>
using namespace std ;
const int mod = 1e9+7 ;
long long p[100010] ;
int main () {
        p[0] = 1 ;
        for( int i = 1 ; i <= 100000 ; ++i ) p[i] = p[i-1] * i % mod ;
        int _ ; cin >> _ ;
        while( _-- ) {
                int n ; cin >> n ;
                cout << ( p[n] - 1 + mod ) % mod << endl ;
        }
        return 0 ;
}
View Code

B:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1526

题意:读了好久,难明 , 一个人从 1 出发要到达 n  。

问1 : 他100%可以到达n 则输出 PARDON , 否则PRISON

问2 : 从1的可以走的存在一条无限长得路(有环),则输出UNLIMITED ,  否则LIMITED

先对图做一次Tarjan进行缩点。

对于问1 , 只要不存在不是n的叶子并且他可以到达n即可。

问2 , 在他从1能够到达的地方不存在环,则输出LIMITED , 否则输出UNLIMITED

#include <bits/stdc++.h>
using namespace std ;
const int N = 52013;
const int M = 5000010;
int n ;
int eh[N] , et[M] , nxt[M] , etot ;
bool f1 , f2  , tag[N] ;
void init() {
    f1 = f2 = true ;
    memset( eh , -1 , sizeof eh ) ;
    etot = 0 ;
}
 
void addedge( int u , int v ) {
    et[etot] = v ; nxt[etot] = eh[u] ; eh[u] = etot++ ;
}
 
int inf , dis[N];
bool inq[N] ;
 
bool spfa( int s , int e ) {
    memset( dis , 0x3f , sizeof dis ) ;
    memset( inq , false , sizeof inq ) ;
    inf = dis[s] ;
    queue<int>que;
    inq[s] = true ;
    dis[s] = 0 ;
    que.push(s);
    while( !que.empty() ) {
        int u = que.front() ;
        que.pop() ;
        bool t = false ;
        for( int i = eh[u] ; ~i ; i = nxt[i] ) {
            t = true ;
            int v = et[i] ;
            if( dis[v] > dis[u] + 1 ) {
                dis[v] = dis[u] + 1 ;
                if( !inq[v] ) {
                    inq[v] = true ;
                    que.push(v) ;
                }
            }
        }
        if( u != e && !t ) f1 = false ;
    }
    if( dis[e] < inf ) return true ;
    return false ;
}
 
// ----------------------------------------
 
int Low[N] , DFN[N] , Stack[N] , Belong[N] ;
bool Instack[N] , it_circle[N];
int Index , top ;
int scc ;
int num[N] ;
void Tarjan( int u ) {
    int v ;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u ;
    Instack[u] = true ;
    for( int i = eh[u] ; ~i ; i = nxt[i] ) {
        v = et[i] ;
        if( !DFN[v] ) {
            Tarjan(v);
            Low[u] = min( Low[u] , Low[v] ) ;
        } else if( Instack[v] && Low[u] > DFN[v] ) {
            Low[u] = DFN[v] ;
        }
    }
    if( Low[u] == DFN[u] ) {
        scc++ ;
        do {
            v = Stack[--top] ;
            Instack[v] = false ;
            Belong[v] = scc ;
            if( tag[v] ) it_circle[scc] = true ;
            num[scc]++;
        } while( v != u ) ;
    }
}
 
vector<int>g[N] ;
void Solve() {
    memset( DFN , 0 , sizeof DFN ) ;
    memset( num , 0 , sizeof num ) ;
    memset( Instack , false , sizeof Instack ) ;
    memset( it_circle , false , sizeof it_circle ) ;
    Index = top = scc = 0 ;
    for( int i = 1 ; i < n ; ++i )
        if( !DFN[i] ) Tarjan(i) ;
 
    for( int i = 1 ; i <= scc ; ++i ) g[i].clear() ;
    for( int u = 1 ; u < n ; ++u ) {
        for( int i = eh[u] ; ~i ; i = nxt[i] ) {
            int v = et[i] ;
            if( Belong[u] == Belong[v] ) continue ;
            g[ Belong[u] ].push_back( Belong[v] ) ;
        }
    }
 
    init() ;
    for( int i = 1 ; i <= scc ; ++i ) {
        for( int j = 0 ; j < g[i].size() ; ++j ) {
            addedge( i , g[i][j] ) ;
        }
    }
}
 
int Run() {
    while( cin >> n ) {
        init();
        memset( tag , false , sizeof tag ) ;
        for( int u = 1 ; u < n ; ++u ) {
            int m ; cin >> m ;
            while( m-- ) {
                int v ; cin >> v ;
                if( u == v ) tag[u] = true ;
                addedge( u , v ) ;
            }
        }
        Solve() ;
        if( spfa( Belong[1] , Belong[n] ) && f1 ) cout << "PARDON " ;
        else cout << "PRISON " ;
        for( int i = 1 ; i <= scc ; ++i ) if( dis[i] < inf && ( num[i] > 1 || it_circle[i] ) ) f2 = false ;
        if( f2 ) cout << "LIMITED" << endl ;
        else cout << "UNLIMITED" << endl ;
    }
    return 0 ;
}
 
int main () {
//    freopen("in.txt","r",stdin);
    ios::sync_with_stdio(0);
    return Run();
}
View Code

C:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1527

给出n个平面上的点 , x轴坐标递增 , 问从点1出发,最后回到1,去的时候只能往 x 轴坐标递增的方向走, 回来只能走x 轴坐标递减的方向。

问走过的总距离最小是多少

对于中途的一个点x , 要么,他属于1 -> n 途中的 (称为第一类), 要么就是 n -> 1 途中的(称为第二类)

设 dp[i][j] 表示 从 1 到达点 i  , 然后回来的时候 从 j -> 1 ,所需要走的距离的最小值 ~

假设 i > j 吧 ,对点 i + 1 就可以进行两种情况的转移。 将它归为两类其一

       i < j  ,亦然

转移方程见代码

#include <bits/stdc++.h>
using namespace std ;
const int N = 520;
const double inf = 1e17 ;
int n ;
struct node{
    double x , y ;
}p[N];

inline double Dis( int a , int b ) {
    return sqrt( ( p[a].x - p[b].x )*( p[a].x - p[b].x ) + ( p[a].y - p[b].y )*( p[a].y - p[b].y ) ) ;
}

double dp[N][N] ;

double Gao() {

    for( int i = 0 ; i <= n ; ++i )
        for( int j = 0 ; j <= n ; ++j )
            dp[i][j] = inf ;

    dp[1][0] = dp[0][1] = 0 ;

    for( int i = 1 ; i < n ; ++i ) {
        for( int j = 0 ; j < i ; ++j ) {

            dp[i+1][j] = min( dp[i+1][j] , dp[i][j] + Dis( i , i + 1 ) ) ;
            dp[i][i+1] = min( dp[i][i+1] , dp[i][j] + Dis( j , i + 1 ) ) ;

            dp[j][i+1] = min( dp[j][i+1] , dp[j][i] + Dis( i , i + 1 ) ) ;
            dp[i+1][i] = min( dp[i+1][i] , dp[j][i] + Dis( j , i + 1 ) ) ;

        }
    }

    double ans = 1e17 ;
    for( int i = 0 ; i < n ; ++i ) ans = min( ans , dp[n][i] + Dis( i , n ) ) ;
    for( int i = 0 ; i < n ; ++i ) ans = min( ans , dp[i][n] + Dis( i , n ) ) ;
    return ans ;
}

int Run() {
    int _ ; cin >> _ ;
    while( _-- ) {
        cin >> n ;
        for( int i = 1 ; i <= n ; ++i ) {
            cin >> p[i].x >> p[i].y ;
        }
        p[0].x = p[1].x , p[0].y = p[1].y ;
        printf("%.10lf
",Gao());
    }
    return 0 ;
}

int main () {
    ios::sync_with_stdio(0);
    return Run();
}
View Code

D:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1529

给出一个长度为n圆环,上面的点有权,问你取其中一段使得其中点权和最大 , 

对于圆环的区间,一般把它copy成两段 , 那么可以先处理一个前序和 。

问题就转化成为求这个长度为 2*n 的区间内 前缀和最大 - 前缀和最小 , 并且前缀和最大的要下标要较大 。

还有一个条件是,这两个点的下标不能超过 n 。

那么就可以用一个单调队列来实现了,每次放进一个点的时候维护队列的非递减性 和 队列长度不超过n , 然后就可更新答案

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2000010 ;
LL a[N] , n , id[N] , head , tail ;

int Run() {
    int _ ; cin >> _ ;
    while( _-- ) {
        cin >> n ;
        for(int i=0; i<n; i++) {
            cin >> a[i] ; a[i+n] = a[i];
        }
        for( int i = 1 ; i < 2 *n ; ++i ) a[i] += a[i-1] ;
        head = tail = 0 ;
        LL ans = 0 , now = 0 ;
        for( int i = 0 ; i < 2 * n ; ++i ) {
            while( head < tail && id[head] + n < i ) head++ ;
            while( head < tail && a[i] < a[ id[tail-1] ] ) tail-- ;
            id[tail++] = i ;
            ans = max( ans , a[i] - a[ id[head] ] );
        }
        cout << ans << endl ;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(0);
    return Run() ;
}
View Code

E:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1530

把一个 2^n的东西 , 分成 a , b , a + b = 2^n 。

问要分多少次 。可以把 a ,  b 看成2进制数以后, 对于a , b 其中有 1 的位 , 都要进行1次划分的

结果就是统计 a|b 有多少个 1

不过没发现这个规律的话,直接模拟也可以

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010 ;
typedef long long ll;
LL a , b , n ;
int Run() {
    int _ ; cin >> _ ;
    while( _-- ) {
        cin >> n >> a >> b ;
        LL tmp = a|b , res = 0 ;
        for( int i = 0 ; i <= 62 ; ++i ) {
            if( tmp&(1LL<<i) ) res++ ;
        }
        cout << res << endl ;
    }
    return 0;
}
int main() {
    ios::sync_with_stdio(0);
    return Run() ;
}
View Code

F:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1531

可以在x或y轴设置激光线 , 使得给出的点都能够被覆盖。

这个是一个最小覆盖的问题,每一行可看成结点X , 每一列可以看成Y 。 选择尽量小的点使得,一条边连上X或Y其中一个 。

其值是等于最大匹配的。 直接求一次匈牙利

#include <bits/stdc++.h>
using namespace std ;
const int N = 110;
const double inf = 1e17 ;
int uN , vN ;
int g[N][N] , linker[N] ;
bool used[N] ;
bool dfs( int u ) {
    for( int v = 0 ; v < vN ; ++v ) {
        if( g[u][v] && !used[v] ) {
            used[v] = true ;
            if( linker[v] == -1 || dfs( linker[v] ) ) {
                linker[v] = u ;
                return true ;
            }
        }
    }
    return false ;
}

int hungary() {
    int res = 0 ;
    memset( linker , -1 , sizeof linker ) ;
    for( int u = 0 ; u < uN ; ++u ) {
        memset( used , false , sizeof used ) ;
        if( dfs(u) ) res++ ;
    }
    return res ;
}

int Run() {
    int _ ; cin >> _ ;
    while( _-- ) {
        int n , m , k ;
        cin >> n >> m >> k ;
        uN = n ,vN = m ;
        memset( g , 0 , sizeof g );
        while( k-- ) {
            double x , y ;
            cin >> x >> y ;
            g[(int)floor(x)][(int)floor(y)] = 1 ;
        }
        cout << hungary() << endl ;
    }
    return 0 ;
}

int main () {
    ios::sync_with_stdio(0);
    return Run();
}
View Code

G:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1532

给出一个区间, 要进行区间更新 , 区间查询  , 显然线段树 。

还给出了一个n 表示区间更新到的值不能够大于n ,而且不能够小于0 。 加上一个区间求最即可

#include <bits/stdc++.h>
using namespace std;
#define root 0,c,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lr rt<<1
#define rr rt<<1|1
const int N = 5000000 ;

int lazy[N<<2] , m[N<<2] , M[N<<2] ;
int c , o , n ;

void Build( int l , int r , int rt ) {
    M[rt] = m[rt] = lazy[rt] =0 ;
    if( l == r ) return ;
    int mid = (l+r)>>1;
    Build(lson) , Build(rson);
}

void Up( int rt ) {
    M[rt] = max( M[lr] , M[rr] ); m[rt] = min( m[lr] , m[rr] );
}

void Down( int rt ) {
    if( lazy[rt] ) {
        m[lr] += lazy[rt] ; m[rr] += lazy[rt] ;
        M[lr] += lazy[rt] ; M[rr] += lazy[rt] ;
        lazy[lr] += lazy[rt] ; lazy[rr] += lazy[rt] ;
        lazy[rt] = 0 ;
    }
}

void update( int l , int r , int rt , int L , int R , int v ) {
        if( l == L && r == R ) {
                lazy[rt] += v ; m[rt] += v ; M[rt] += v ;
                return ;
        }
        Down( rt );
        int mid = (l+r)>>1;
        if( R <= mid ) {
                update( lson , L , R , v );
        } else if( L > mid ) {
                update( rson , L ,R , v );
        } else {
                update( lson , L , mid , v ) , update( rson , mid + 1 , R , v );
        }
        Up( rt );
}

int Query( int l , int r , int rt , int L , int R , int tag ) {
        if( l == L && r == R ) {
                if( tag )return M[rt] ;
                else return m[rt] ;
        }
        Down( rt ) ;
        int mid = (l+r)>>1 , res ;
        if( R <= mid ) {
                res = Query( lson , L , R , tag );
        } else if( L > mid ) {
                res = Query( rson , L ,R , tag );
        } else {
                if( tag ) res = max(  Query( lson , L , mid , tag ) ,  Query( rson , mid + 1 , R , tag ) ) ;
                else res = min( Query( lson , L , mid , tag ) ,  Query( rson , mid + 1 , R , tag ) ) ;
        }
        Up(rt);
        return res ;
}

void Gao( int x , int y , int v ) {
        if( v < 0 ) {
                int m = Query( root , x , y , 0 );
                v = 0 - min( -v , m ) ;
        } else {
                int M = Query( root , x , y , 1 );
                v = min( n - M , v ) ;
        }
        cout << v << endl ;
        update( root , x , y , v );
}

int main()
{
        ios::sync_with_stdio(0);
        int x , y , v ; string s ;
        while( cin >> c >> n >> o ) {
                Build(root);
                while( o-- ) {
                        cin >> s >> x ;
                        if( s[0] == 'g' ) {
                                cin >> y >> v ;
                                Gao( x  , y  , v ) ;
                        } else if( s[0] == 'c' ) {
                                cin >> v ;
                                Gao( x  , x  , v ) ;
                        } else {
                                cout << Query( root , x , x , 0 ) << endl ;
                        }
                }
        }
        return 0 ;
}
View Code

H:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1533

几何题 , 给出一些多边形给你,问你多边形是否有自交, 跟另外的多边形相交 , 多边形之间嵌套会大于1层的情况。

否则就输出 CORRECT

那么就是可以暴力枚举两条直线 , 来判相交 。

判一下点跟多边形的关系。

对于嵌套的层数的话,它是一个DAG图, 直接dfs一下即可 。

#include <bits/stdc++.h>
using namespace std ;
const int N = 105 ;
const double eps = 1e-8;

int sgn( double x ) {
    if( fabs(x) < eps ) return 0 ;
    if( x < 0 ) return -1 ;
    else return 1 ;
}

struct Point {
    double x , y ;
    Point(){}
    Point( double x , double y ):x(x),y(y){}
    Point operator - ( const Point &b ) const {
        return Point( x-b.x , y-b.y ) ;
    }
    Point operator + ( const Point &b ) const {
        return Point( x+b.x , y+b.y ) ;
    }
    double operator * ( const Point &b ) const {
        return x*b.x+y*b.y ;
    }
    double operator ^ ( const Point &b ) const {
        return x*b.y - y*b.x ;
    }
    bool operator == ( Point b ) const {
        return sgn( x - b.x ) == 0 && sgn( y - b.y ) == 0 ;
    }
    void input() {
        scanf("%lf%lf",&x,&y);
    }
};

struct Line {
    Point s , e ;
    Line(){};
    Line( Point s , Point e ):s(s),e(e){}
    int segcrossseg( Line v ) {
        int d1 = sgn( (e-s)^(v.s-s) );
        int d2 = sgn( (e-s)^(v.e-s) );
        int d3 = sgn( (v.e-v.s)^(s-v.s) );
        int d4 = sgn( (v.e-v.s)^(e-v.s) );
        if( (d1^d2) == -2 && (d3^d4) == -2 ) return 2 ;
        return ( d1==0 && sgn((v.s-s)*(v.s-e))<=0 ) ||
            ( d2==0 && sgn((v.e-s)*(v.e-e))<=0 ) ||
            ( d3==0 && sgn((s-v.s)*(s-v.e))<=0 ) ||
            ( d4==0 && sgn((e-v.s)*(e-v.e))<=0 ) ;
    }
    bool pointonseg( Point p ) {
        return sgn((p-s)^(e-s)) == 0 && sgn((p-s)*(p-e)) <= 0 ;
    }
};

struct polygon {
    int n ;
    Point p[N] ;
    Line l[N] ;
    void input( int _n ) {
        n = _n ;
        for( int i = 0 ; i < n ; ++i ) {
            p[i].input() ;
        }
    }
    void getline() {
        for( int i = 0 ; i < n ; ++i ) {
            l[i] = Line( p[i] , p[(i+1)%n] );
        }
    }
    // 3 on point
    // 2 on side
    // 1 inside
    // 0 outside
    int relationpoint( Point q ) {
        for( int i = 0 ; i < n ; ++i ) {
            if( p[i] == q ) return 3 ;
        }
        for( int i = 0 ; i < n ; ++i ) {
            if( l[i].pointonseg(q) ) return 2 ;
        }
        int cnt = 0 ;
        for( int i = 0 ; i < n ; ++i ) {
            int j = (i+1)%n ;
            int k = sgn((q-p[j])^(p[i]-p[j]));
            int u = sgn(p[i].y-q.y);
            int v = sgn(p[j].y-q.y);
            if( k > 0 && u < 0 && v >= 0 ) cnt++ ;
            if( k < 0 && v < 0 && u >= 0 ) cnt-- ;
        }
        return cnt != 0 ;
    }
} polygon[N] ;

int n , k ;

vector<int>g[N];
int dfs( int dep , int u ) {
    int tmp = dep ;
    for( int i = 0 ; i < g[u].size() ; ++i ) {
        int v = g[u][i] ;
        tmp = max( tmp , dfs( dep + 1 , v ) ) ;
    }
    return tmp ;
}

int Gao() {
    for( int i = 0 ; i < k ; ++i ) {
        if( polygon[i].p[0] == polygon[i].p[ polygon[i].n - 1 ] ) {
            polygon[i].n-- ;
            polygon[i].getline();
            for( int j = 0 ; j < polygon[i].n ; ++j ) {
                for( int y = 0 ; y < j - 1 ; ++y ) {
                    if( j == polygon[i].n - 1 && y == 0 ) continue ;
                    if( polygon[i].l[j].segcrossseg( polygon[i].l[y] ) ) {
                        return 0 ;
                    }
                }
            }
         } else {
            return 0 ;
         }
    }
    for( int i = 0 ; i < k ; ++i ) {
        for( int j = 0 ; j < i ; ++j ) {
            for( int y = 0 ; y < polygon[i].n ; ++y ) {
                for( int z = 0 ; z < polygon[j].n ; ++z ) {
                    Line l1 = Line( polygon[i].p[y] , polygon[i].p[y+1] );
                    Line l2 = Line( polygon[j].p[z] , polygon[j].p[z+1] );
                    if( l1.segcrossseg(l2) ) return 1 ;
                }
            }
        }
    }

    for( int i = 0 ; i < k ; ++i ) {
        g[i].clear();
        for( int j = 0 ; j < k ; ++j ) {
            if( i == j ) continue ;
            bool f = true ;
            for( int y = 0 ; y < polygon[j].n && f ; ++y ) {
                if( ! polygon[i].relationpoint( polygon[j].p[y] ) ) f = false ;
            }
            if( f ) g[i].push_back(j) ;
        }
    }
    for( int i = 0 ; i < k ; ++i ) if( dfs( 0 , i ) > 1 ) {
        return 2 ;
    }
    return 3 ;
}

int Run() {
    int _ ; scanf("%d",&_);
    while( _-- ) {
        scanf("%d",&k);
        for( int i = 0 ; i < k ; ++i ) {
            scanf("%d",&n);
            polygon[i].input(n);
        }
        int ans = Gao() ;
        if( ans == 0 ) puts("INVALID POLYGON");
        else if( ans == 1 ) puts("INTERSECTING POLYGONS");
        else if( ans == 2 ) puts("INVALID NESTING");
        else puts("CORRECT");
    }
    return 0 ;
}

int main () {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif // LOCAL
    ios::sync_with_stdio(0);
    return Run();
}
View Code

I:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1534

没补

J:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1535

给出一个n序列 , 一个数 k 

有3种人,第一类人抽最大数,第二类人抽最小数,第3类人随意抽数。

问最后能否剩下第k小的那个数。

可以发现 除去 第k小的那个数外 的n-1个数可以进行 cnt = (n-1)/ 3 轮 抽数,

即 必须符合 x >=  cnt , y >= cnt .

当 x == cnt 的时候 y 多出来的不可以超过2 。否则在第 cnt + 1 轮,第二类人会把第k小的那个数抽了

当 x == cnt 的时候 x 多出来的不可以超过1 。否则在第 cnt + 1 轮,第一类人会把第k小的那个数抽了

直接模拟也可。

#include <bits/stdc++.h>
using namespace std ;
const int N = 100010;
int Run() {
    int x , n , k ; string s ;
    while( cin >> n >> k ) {
        for( int i = 1 ; i <= n ; ++i ) cin >> x >> s ;
        int x = k - 1  , y = n - k ;
        n-- ;
        if( (x < n/3 ) || (y < n/3) ) puts("NO") ;
        else {
            if( y == n/3 && n % 3 ) puts("NO") ;
            else if( x == n/3 && n % 3 == 2 ) puts("NO") ;
            else puts("YES");
        }
    }
    return 0 ;
}

int main () {
    ios::sync_with_stdio(0);
    return Run();
}
View Code
原文地址:https://www.cnblogs.com/hlmark/p/4428317.html