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 ; }
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(); }
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(); }
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() ; }
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() ; }
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(); }
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 ; }
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(); }
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(); }