2013长沙全国邀请赛 总结
水平太渣. 唯有看完解题报告+标程 AK这套题.
A CircleGame.
需要取上整,核心点加上了一个小于1的小数.
当n = 2时,有 , 然后可以验证下当n取其他值时也满足。所以就有
又 , 则其特征方程的两个解为:
当 n = 2. 时, 其特征方程为:
化简得到:
因为
所以有:
S_2 - 2*a*S_1 + (a^2+b) = 0
得到线性递推关系, 然后矩阵快速幂随便搞搞就好了。
// Sn = ceil( [a + sqrt(b)]^n ) % m; #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; struct Matrix{ LL mat[2][2]; void zero(){memset(mat,0,sizeof(mat));} void unit(){zero();mat[0][0]=mat[1][1]=1;} }A,T; Matrix mult( Matrix a, Matrix b, int mod ){ Matrix c; c.zero(); for(int i = 0; i < 2; i++) for(int k = 0; k < 2; k++) for(int j = 0; j < 2; j++) { c.mat[i][j] += a.mat[i][k]*b.mat[k][j]; c.mat[i][j] %= mod; } return c; } Matrix Pow( Matrix x, int n, int mod ){ Matrix c; c.unit(); while(n){ if(n&1) c = mult(c,x,mod); x = mult(x,x,mod); n >>= 1; } return c; } void init(int a,int b){ int a1 = 2*a, b1 = b-a*a; T.mat[0][0] = 0; T.mat[0][1] = 1; T.mat[1][0] = b1; T.mat[1][1] = a1; A.zero(); A.mat[0][0] = 2; A.mat[1][0] = 2*a; } int main(){ int a,b,n,m; while( scanf("%d%d%d%d",&a,&b,&n,&m) != EOF){ init(a,b); T = Pow( T, n, m ); A = mult( T, A, m ); printf("%lld\n", (A.mat[0][0]+m)%m ); } return 0; }
D. Hunter
题意描述有点问题,应该问是否能够拿完全部宝藏,若可以则输出最小花费,否则为0. (事实上不存在宝藏拿不了的数据,保证有解).
解法是状态压缩. 整体思路:
因为考虑到宝藏最多13个,则状态数量最多 2^13 = 8*1024, 而且拿宝藏的过程必定是一个一个拿.所以我们可以通过枚举线路.得到最优值.
状态方程: dp( mask, i ) 表示当前状态,0/1分别表示对应宝藏未拿与拿,i表示最后一个拿的i宝藏.
转移方程:
dp( mask, j ) = Min{ dp( mask^(1<<j) , i ) + cost( i, j ) } , 其中 cost(i,j)表示从I到J得最小花费.
初始化, dp( 1<<i, i ) = Min{ cost( i, 边界 ) }
我的做法是 跑了K次SPFA,存储到数组 dist( i, x, y ), 表示从第i个宝藏出发到达点(x,y)的最短距离。
#include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #include<vector> #include<algorithm> using namespace std; const int MASK = (1<<13) + 10; const int N = 210; const int inf = 0x3f3f3f3f; int dis[15][N][N]; bool vis[N][N]; int dp[MASK][15]; int n, m, K; int mp[N][N], cost[15]; int dir[4][2] = { {-1,0},{1,0},{0,-1},{0,1} }; bool legal(int x,int y){ if( x>=0 && x<n && y>=0 && y<m ) return true; return false; } struct Point{ int x,y; Point(){} Point(int _x,int _y):x(_x),y(_y){} }p[15]; void init(){ for(int i = 0; i < K; i++){ queue< Point > Q; memset(vis,0,sizeof(vis)); memset( dis[i], 0x3f, sizeof(dis[i])); dis[i][ p[i].x ][ p[i].y ] = 0; Q.push( Point(p[i].x, p[i].y) ); vis[ p[i].x ][ p[i].y ] = true; while( !Q.empty() ){ Point now = Q.front(); Q.pop(); int x = now.x, y = now.y; vis[x][y] = false; for(int j = 0; j < 4; j++){ int x1 = x+dir[j][0], y1 = y+dir[j][1]; if( legal(x1,y1) && (mp[x1][y1]!=inf) && (dis[i][x1][y1] > dis[i][x][y] + mp[x1][y1]) ){ dis[i][x1][y1] = dis[i][x][y] + mp[x1][y1]; if( !vis[x1][y1] ) Q.push( Point(x1,y1) ), vis[x1][y1] = true; } } } } for(int i = 0; i < K; i++){ cost[i] = inf; for(int row = 0; row < n; row++) cost[i] = min( cost[i], min(dis[i][row][0], dis[i][row][m-1]) ); for(int col = 0; col < m; col++) cost[i] = min( cost[i], min(dis[i][0][col], dis[i][n-1][col]) ); } } void solve(){ memset( dp, 0x3f, sizeof(dp) ); for(int i = 0; i < K; i++) dp[1<<i][i] = cost[i] + mp[ p[i].x ][ p[i].y ]; int Mask = 1<<K; for(int mask = 1; mask < Mask; mask++ ){ for(int i = 0; i < K; i++){ for(int j = 0; j < K; j++){ if( (mask&(1<<i)) && (mask&(1<<j)) && (i!=j) ){ if( dp[mask^(1<<j)][i] < inf ) dp[mask][j] = min( dp[mask][j], dp[mask^(1<<j)][i] + dis[i][ p[j].x ][ p[j].y ] ); } } } } int res = inf; for(int i = 0; i < K; i++){ res = min( res, dp[Mask-1][i] + cost[i] ); } if( res < inf ) printf("%d\n", res ); else printf("0\n"); } int main(){ int _; scanf("%d", &_); while( _-- ){ scanf("%d%d",&n,&m); for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ scanf("%d", &mp[i][j]); if( mp[i][j] == -1 ) mp[i][j] = inf; } } scanf("%d",&K); int a,b; for(int i = 0; i < K; i++){ scanf("%d%d",&a,&b); p[i] = Point(a,b); } init(); solve(); } return 0; }
G. Travel_in_Time
题目描述要看清楚,
重点: 当前访问顶点u, 下一个顶点v, 需要满足条件 val[u] < val[v] .
然后就是用Floyd处理任意两点的最短距离,可以看成是经过但不访问的花费。然后之后的dp[u][T]过程就看做是必须访问u点。
那么还有个问题是,起点S,E不一定要访问,所以需要虚拟一个起点和一个终点。然后随便搞搞就好了。
#include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #include<algorithm> using namespace std; const int N = 110; const int M = 310; const int inf = 0x3f3f3f3f; typedef pair<int,int> point; #define MIN(a,b) (a)<(b)?(a):(b) #define MAX(a,b) (a)>(b)?(a):(b) #define min MIN #define max MAX int dis[N][M]; int n, m, T, S, E; int cost[N], val[N]; int mp[N][N]; bool vis[N][M]; void solve(){ memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); vis[0][0] = true; queue< point > Q; Q.push( make_pair(0,0) ); while( !Q.empty() ){ point now = Q.front(); Q.pop(); int u = now.first, t = now.second; vis[u][t] = false; for(int v = 0; v < n; v++){ if( v == u ) continue; if( mp[u][v] < inf ){ int tn = t + mp[u][v] + cost[v]; if( (tn <= T) && (dis[v][tn] < dis[u][t] + val[v]) ){ if( (v == n-1) || (val[v] > val[u]) ){ dis[v][tn] = dis[u][t] + val[v]; if( !vis[v][tn] ) // val[v] must greate than zero. Q.push( make_pair(v,tn) ), vis[v][tn] = true; } } } } } int res = 0; for(int t = 0; t <= T; t++ ){ res = max( res, max(dis[E][t],dis[n-1][t]) ); } printf("%d\n", res); } int main(){ freopen("Travel_in_Time.in","r",stdin); freopen("test.out","w",stdout); int _; scanf("%d",&_); for(int Case = 1; Case <= _; Case++ ){ scanf("%d%d%d%d%d",&n,&m,&T,&S,&E); S++; E++; memset(mp,0x3f,sizeof(mp)); for(int i = 1; i <= n; i++) scanf("%d", &cost[i]); for(int i = 1; i <= n; i++) scanf("%d", &val[i]); int a, b, c; for(int i = 0; i < m; i++){ scanf("%d%d%d",&a,&b,&c); a++; b++; mp[a][b]=mp[b][a]=min( mp[a][b],c ); } // Floyd for(int k = 1; k <= n; k++ ) for(int i = 1; i <= n; i++ ) for(int j = 1; j <= n; j++ ) mp[i][j] = min( mp[i][j], mp[i][k] + mp[k][j] ); // create virtual point of n; for(int i = 1; i <= n; i++){ if( mp[E][i] < inf ) mp[i][n+1] = mp[E][i]; if( mp[S][i] < inf ) mp[0][i] = mp[S][i]; } mp[0][S] = 0; mp[E][n+1] = 0; cost[0] = 0; val[0] = -1; cost[n+1] = 0; val[n+1] = 0; n += 2; printf("Case #%d:\n", Case); solve(); } return 0; } /* 1 4 4 0 0 3 1 1 1 1 5 7 9 12 0 1 1 1 3 1 0 2 1 2 3 1 */
H.Bottles Arrangement
证明比较费力, 比赛过程猜结论感觉比较靠谱.
令 n = 2*L+1, 假定存在一行i, 且 A( i, L+1 ) = m.
考虑, A( i, L ) 与 A( i, L+2 ) ,若A(i, L) = m,
则因为 相邻之差绝对值小于等于1, 就有
A(i,L-1) >= m-1, A(i, L-2) >= m-2, ..., A(i, 1) >= m-L+1
A(i,L+2) >= m-1, A(i, L+3) >= m-2, ..., A(i,2L+1) >= m-L
求和得出 \sum { A(i, j) } >= (2*L+1)*M - L*L = N*M - L*L
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; typedef long long LL; int main(){ int m, n; while( scanf("%d%d",&m,&n) != EOF ){ int l = n/2; printf("%d\n", n*m-l*l ); } return 0; }