2013-5-20 训练赛后总结

A, 背景为麻将的模拟题，按照要求模拟就好。

B，sqrt(N)分解因子，然后暴力算即可

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

int sum(int x,int b){
int res = 0;
while(x){
res += (x%b)*(x%b);
x /= b;
}
return res;
}
char mp[50] = "0123456789ABCDEF";

int main(){
int n, m;
while( scanf("%d%d", &n,&m) != EOF){
int ans = 0;
for(int i = 1; i*i <= n; i++){
if( n%i == 0 ){
if( i*i == n ){
ans += sum(i,m);
}
else{
ans += sum(i,m);
ans += sum(n/i,m);
}
}
}
int a[50], n1 = 0;
while(ans){
a[n1++] = ans%m;
ans /= m;
}

for(int i = n1-1; i >= 0; i--){
printf("%c", mp[a[i]]);
} puts("");
}
return 0;
}
View Code

C，dp( L, x, y ) 表示 前L-2位完全匹配，L-1,L分别为x,y的方案数， 然后转移有三类：

L-1 / L / L+1,     三个都单独转，两个一起， 三个一起， 推一下就可以得到O(1)计算出来

转移方程可以写成  dp( L+1, y1, z )  = Min{ dp(L, x, y ) + cost(三个变换)  }

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)<(b)?(a):(b)
#define count Count
const int N = 1010;
int s[1005], e[1005];
char str[1005], str2[1005];
int n, dp[1005][11][11];
const int INF = 0x3f3f3f3f;
int c3[10][10][10][10][10][10];
int c2[10][10][10][10];
int c1[10][10];

int count(int x1, int x2, int up) {
if (up) {
if (x2 >= x1) return x2 - x1;
else return 10-(x1 - x2);
} else {
if (x2 <= x1) return x1 - x2;
else return 10-(x2 - x1);
}
}
int change1(int x1,int x2){
return min( count(x1,x2,1), count(x1,x2,0) );
}
int change2(int x1,int x2,int y1,int y2){
int x_up = count(x1,x2,1), y_up = count(y1,y2,1);
int x_down = count(x1,x2,0), y_down = count(y1,y2,0);
return min( max(x_up,y_up), max(x_down,y_down) );
}
int change3(int x1,int x2,int y1,int y2,int z1,int z2){
int x_up = count(x1,x2,1), y_up = count(y1,y2,1), z_up = count(z1,z2,1);
int x_down = count(x1,x2,0), y_down = count(y1,y2,0), z_down = count(z1,z2,0);
int res = INF;
if( y_up<=x_up && y_up<=z_up ) res = min(res,min(10+y_up,x_up+z_up-y_up));
else res = min( res, max(x_up,max(y_up,z_up)) );
if( y_down<=x_down && y_down<=z_down ) res = min(res,min(10+y_down,x_down+z_down-y_down));
else res = min( res, max(x_down,max(y_down,z_down)));
}

int comp(int x1,int x2,int y1,int y2,int z1,int z2){
int t = INF;
t = min( t, c1[x1][x2] + c1[y1][y2] + c1[z1][z2] );
t = min( t, c1[x1][x2] + c2[y1][y2][z1][z2] );
t = min( t, c2[x1][x2][y1][y2] + c1[z1][z2] );
t = min( t, c3[x1][x2][y1][y2][z1][z2] );
//t = min( t, change1(x1,x2)+change1(y1,y2)+change1(z1,z2) );
//t = min( t, change1(x1,x2)+change2(y1,y2,z1,z2) );
//t = min( t, change2(x1,x2,y1,y2)+change1(z1,z2) );
//t = min( t, change3(x1,x2,y1,y2,z1,z2) );
return t;
}
void init(){
for(int x1 = 0; x1 < 10; x1++){
for(int x2 = 0; x2 < 10; x2++){
c1[x1][x2] = change1(x1,x2);
for(int y1 = 0; y1 < 10; y1++){
for(int y2 = 0; y2 < 10; y2++){
c2[x1][x2][y1][y2] = change2(x1,x2,y1,y2);
for(int z1 = 0; z1 < 10; z1++){
for(int z2 = 0; z2 < 10; z2++){
c3[x1][x2][y1][y2][z1][z2] = change3(x1,x2,y1,y2,z1,z2);
}
}
}
}
}
}
}
void solve() {
memset(dp,0x3f,sizeof(dp));
for(int y = 0; y < 10; y++){
dp[1][0][y] = change1(s[1],y);
}
for(int L = 1; L < n; L++)
{
for(int x = 0; x < 10; x++)
for(int y = 0; y < 10; y++)    {
if( dp[L][x][y] == INF ) continue;
for(int y1 = 0; y1 < 10; y1++)
for(int z = 0; z < 10; z++){
int t = comp(x,e[L-1],y,y1,s[L+1],z );
dp[L+1][y1][z] = min( dp[L+1][y1][z], dp[L][x][y] + t );
}
}
}

printf("%d\n", dp[n][e[n-1]][e[n]] );
}

int main() {
//    printf("(5,2,1) = %d, (2,2,1) = %d\n", change3(5,9,2,9,1,5), change3(2,9,2,9,1,5) );
init();
while (scanf("%s %s", str + 1, str2 + 1) != EOF) {
n = strlen(str+1);
s[0] = e[0] = 0;
for (int i = 1; i <= n; ++i) {
s[i] = str[i] - '0';
e[i] = str2[i] - '0';
}
solve();
}
return 0;
}
View Code

D, N个点围成一圈， 每个点有个权值，可以修改： i变为 -Ai,  i-1与i+1位置+Ai, 不会捉。。。

E. 第i个城市建加油站花费为 2^i, 比 (1,i-1)全部建加油站的和还大，可见，若i城市不建加油站而在 (1,i-1)这些位置多建一些这样更优。 那么我们可以最初令所有城市都建，然后从N到1开始尝试删除加油站，看是否合法。

然后就是删除一个加油站判定问题， 因为未规定一个点只能走一次，要求长度最大为K， 那么当一个点从一个有加油站的城市出发，要么到有加油站的城市，或者到没有加油站的城市， 此时若全部点需要走到，并且能够回到1， 则必定在那些没有加油站的城市能够返回，那么此时从加油站的城市到非加油站的城市 路径长度必须要少于或等于 K/2，这样才能保证返回，另外 从有加油站的城市 到 有加油站的城市 则只要距离小于等于K即可。必定可以返回。 另外，我们假定这样一个结论： 所有非加油站城市 都能通过至少一个 加油站城市 到达。 因为若 从一个 加油站城市 经过 多个非加油站城市 再到 加油站城市， 其路径总长度必定小于等于K， 那么意味着 这两个加油站城市 直接距离必定小于等于K，意味着我们不需要通过 非加油站 城市到达 加油站城市。  所以我们能够通过 BFS，若是加油站城市则 入队，然后判定是否能够走到所有顶点至少一次。

#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;

const int N = 150;
const int inf = 0x3f3f3f3f;
struct node{
int x, y;
}city[N];

int mp[N][N];
bool vis[N], sta[N];
int n, K;

int dist(int i,int j){
return ceil( sqrt( (city[i].x-city[j].x)*(city[i].x-city[j].x)+(city[i].y-city[j].y)*(city[i].y-city[j].y) ) );
}
bool legal(){
queue<int>Q; while( !Q.empty() ) Q.pop();
memset(vis,0,sizeof(vis));
Q.push(0);
vis[0] = true;
while( !Q.empty() ){
int cur = Q.front(); Q.pop();
for(int nxt = 0; nxt < n; nxt++){
if( cur == nxt ) continue;
if( !vis[nxt] ){
if( sta[nxt] ){
if( mp[cur][nxt] <= K )
vis[nxt] = true, Q.push(nxt);
}
else{
if( mp[cur][nxt] <= K/2 )
vis[nxt] = true;
}
}
}
}
for(int i = 0; i < n; i++)
if( !vis[i] ) return false;
return true;
}
int main(){
while( scanf("%d%d",&n,&K) != EOF){
for(int i = 0; i < n; i++){
scanf("%d%d", &city[i].x, &city[i].y );
}
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++){
if( i == j ) mp[i][j] = inf;
else mp[i][j]=mp[j][i]=dist(i,j);
}
for(int i = 0; i < n; i++) sta[i] = true;
if( legal() == false ){ puts("-1"); continue; }
else{
for(int i = n-1; i > 0; i-- ){
sta[i] = false;
if( !legal() ) sta[i] = true;
}
}
int k;
for(k = n-1; k > 0; k--)
if( sta[k] ) break;
for( ; k >= 0; k-- )
printf("%d",sta[k]);
puts("");
}
return 0;
}
View Code

F.  所有字串问题，听说后缀数组能做，用后缀自动机写会方便。果断去学习下。。。。

G. swap， 依据定义神码是 cup,  虽然矩阵很小，但是还是不知道如何计算。

H.  对于AB顺序，求出X获得 A，B，A+B分的期望， 与 BA顺序，获得A，B，A+B的期望，比较下即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
const double esp = 1e-8;
double sign(double x){
return x<-esp?-1:(x>esp);
}
int main(){
int T;
scanf("%d", &T);
while( T-- ){
int A, B;
double P, Q;
scanf("%d%d%lf%lf", &A,&B,&P,&Q);
double P1 = 1-P, Q1 = 1-Q;
double tiger = A*( Q1+Q*P*P1 )+B*(Q*P*P1)+(A+B)*(Q*P*P);
double wolf = A*(Q1*P1*P) + B*(Q+Q1*P*P1) + (A+B)*(Q1*P*P);
if( sign(tiger-wolf) >= 0 ){
printf("tiger %.4lf\n", tiger );
}
else printf("wolf %.4lf\n", wolf );
}
return 0;
}
View Code

I.  题目没看，看到别人题解似乎是一道数学题。。。

J. 给定的是多个连通块，然后连通块内部顶点有边，边上有权值， 然后不同连通块间只能坐地铁，问坐地铁不超过K次游览各个连通快最大权值。。。暂时无思路。。。

K.  使用数据结构 Splay Tree 来维护， 题目主要要解决 插入-x位置问题， 因为要满足 队列进出序列，意味着 +x之前的正整数对应的出队操作完成后-x才能出去，之间还可以有一些 数+y进队，但是-x一定要在这些+y出队前出去，所以可以得出 -x的位置，假定  +x左边的正整数的数量为 k,  则-x的位置应该为 左数第k+1个负数位置前一位， 若序列无k+1个负数，则在最后一位。 解决这个问题就好做了。 因为要序列中未出现的最小正整数，我们用个堆放进1-n。然后每次insert就删掉一个最小，若remove就将x加入进去。logN即可。

insert i ,操作， 通过将 i-1旋转到根，再将i旋转到根下，然后插入的节点就是i的左儿子了。这很好做，-x问题就是上面说到的，先找到位置，找到了就与插入+x是一样的了。查找第K+1的负数，与整数的问题，我们可以通过添加一个节点信息来保存子树 负数个数，正数个数来解决。 多个标记即可。还有就是为了方便下面的remove 与 query操作，我们将 键值为 x的 +x,-x在 splay中的指针记录起来，这样就方便 对其进行删除与查询了。我的程序是用 loc[x][2] 分别记录 x, -x 对应的节点指针 p。

remove x,  因为前面记录了loc[x][2],其+x,-x在splay中的节点指针， 则我们对于+x删除， 将其左边第一个与右边第一个分别移动到根与根下，然后删除左儿子即可。注意要往上push_up;

query x, 其实和remove差不多， 如果不太理解可以看看关于 splay维护 数列区间的论文。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<string>
#include<algorithm>
using namespace std;
#define keytree ch[ ch[rt][1] ][0]
#define ft ch[rt][1]
const int N = (int)4e5+100;
typedef long long LL;
struct Splay{
int ch[N][2],pre[N],sz[N];
int num[N][2], key[N];
LL sum[N];
int rt, top;
void rotate(int x,int d){
int y = pre[x];
ch[y][d^1] = ch[x][d]; pre[ ch[x][d] ] = y;
if( pre[y] ) ch[pre[y]][ ch[pre[y]][1] == y ] = x;
pre[x] = pre[y]; ch[x][d] = y; pre[y] = x;
push_up(y);
}
void splay(int x,int goal){
while( pre[x] != goal ){
int y = pre[x];
if( pre[y] == goal ) rotate( x, ch[y][0]==x );
else{
int z = pre[y];
int d1 = ch[z][0]==y, d2 = ch[y][0]==x;
if( d1 == d2 ) rotate(y,d1), rotate(x,d2);
else rotate(x,d2), rotate(x,d1);
}
}
if( goal == 0 ) rt = x;
push_up(x);
}
void rotate_to(int k,int goal){ //左边有k个，不包含自身的情况下
int x = rt;
while( sz[ ch[x][0] ] != k ){
if( sz[ ch[x][0] ] >= k ) x = ch[x][0];
else k -= (sz[ch[x][0]]+1), x = ch[x][1];
}
//printf("x = %d\n", x );
splay( x, goal );
push_up(x);
}
void NewNode(int &x, int c, int f){
x = ++top;
ch[x][0] = ch[x][1] = 0;
pre[x] = f; sz[x] = 1;
num[x][0] = (c>0); num[x][1] = (c<0);
key[x] = sum[x] = c;
}
void init(){
// init the null point
ch[0][0]=ch[0][1]=pre[0]=sz[0]=0;
num[0][0]=num[0][1]=key[0]=sum[0]=0;
top = rt = 0;

// Two Virtual Point
NewNode( rt, 0, 0 );
NewNode( ch[rt][1], 0, rt );
push_up(rt);

//    print(rt);
}

int Kth(){
int k = num[ ch[rt][0] ][0] + 1; //左数第n+1个负数位置
if( num[rt][1] < k ) return sz[rt]-1;
else{
int x = rt, s = 0;
while(1){
int d = num[ ch[x][0] ][1];
if( d >= k ) x = ch[x][0];
else if( d+(key[x]<0) == k )
return (s+sz[ ch[x][0] ]);
else{
s += 1+sz[ ch[x][0] ];
k -= ( d + ( key[x] < 0 ) );
x = ch[x][1];
}
}
}
}
void push_up(int x){
if(x){
sum[x] = key[x] + sum[ ch[x][0] ] + sum[ ch[x][1] ];
sz[x] = 1 + sz[ ch[x][0] ] + sz[ ch[x][1] ];
num[x][0] = (key[x]>0) + num[ ch[x][0] ][0] + num[ ch[x][1] ][0];
num[x][1] = (key[x]<0) + num[ ch[x][0] ][1] + num[ ch[x][1] ][1];
}
}
int insert(int val,int pos){
//printf("Insert: pos = %d\n", pos );
rotate_to( pos-1, 0 );
rotate_to( pos, rt );
NewNode( keytree, val, ft );
push_up( ft ), push_up(rt);
//printf("keytree =  %d\n", keytree );
splay( keytree, 0 );

return rt;
}
void remove(int x){
splay(x, 0);
int lch = ch[x][0], rch = ch[x][1];
while( ch[lch][1] ) lch = ch[lch][1];
while( ch[rch][0] ) rch = ch[rch][0];

splay( lch, 0 ); splay( rch, rt );
ch[ft][0] = 0;
push_up(ft); push_up(rt);

}
LL query(int x, int y){
splay(x,0); splay(y,rt);
return sum[keytree];
}
void print(int x){
if( ch[x][0] ) print( ch[x][0] );
printf("节点指针%d, 键值:%d, 父节点:%d, 左儿子:%d, 右儿子:%d,sum = %lld, sz = %d, num[0] = %d, num[1] = %d\n",
x, key[x], pre[x], ch[x][0], ch[x][1], sum[x], sz[x],
num[x][0], num[x][1] );
if( ch[x][1] ) print( ch[x][1] );
}
}spt;

int loc[N][2];

int main(){
freopen("1.in","r",stdin);
int n, Case = 1;
while( scanf("%d", &n) != EOF){
printf("Case #%d:\n", Case++ );
// init
spt.init();
priority_queue<int,vector<int>,greater<int> > Q;
for(int i = 1; i <= n; i++) Q.push(i);
// operate
char op[10];
int x, pos;
for(int i = 0; i < n; i++){
scanf("%s", op);
if( op[0] == 'i' ){ //insert pos
scanf("%d", &pos);
x = Q.top(); Q.pop();
loc[x][0] = spt.insert( x, pos+1 ); // must splay(x,0) ,the rt is x
//    spt.print( spt.rt ); puts("*******");

pos = spt.Kth();
loc[x][1] = spt.insert( -x, pos );
//    spt.print( spt.rt );
}
else if( op[0] == 'r' ){ //remove x
scanf("%d", &x);
spt.remove( loc[x][1] );
spt.remove( loc[x][0] );
Q.push(x);
}
else{ // query x
scanf("%d", &x);
LL res = spt.query( loc[x][0], loc[x][1] );
printf("%lld\n", res );
}
}
}
return 0;
}
View Code

posted @ 2013-05-22 12:13  yefeng1627  阅读(192)  评论(0编辑  收藏  举报