分类目录归档:ACM

UVa 11300 Spreading the Wealth 题解

UVa 11300, 此题用了代数法转换模型为:给定数轴上的n个点,求某一点使得到该数轴上各点距离之和最小。

令x[i]表示第i个人给了第i-1个人多少金币。特别地,x[1]表示第一个人给第n个人的。

令M表示最后每人赢得的金币数即平均值。

a[i]表示开始时所持金币数。

则有:

x1 = x1

x2 = M – a[1] + x[1] = x1 + M – a[1]

x3 = M – a[2] + x[2] = 2M – a[1] – a[2] + x[1]

x4 = M – a[3] + x[3] = 3M – a[1] – a[2] – a[3] + x1

令C[n] = sigma(a[i]) – n*m

则有 x[n] = x1 – c[n]

所求问题转化为:求x[1]使得 |x1| + |x1- C[1]| + |x1 – C[2]| … |x1 – C[n-1]|最小。

# include <iostream>
# include <cstdio>
# include <algorithm>
# define rep(i, n, x) for ( int i = 0; i < n; i+=x )
using namespace std;

int n;
long long A[1000007], sum, M, C[1000007];
long long ans;

long long labs(long long a)
{
    return a > 0LL ? a: -a;
}

int main()
{
    while ( cin >> n )
    {
        sum = 0;
        rep(i, n, 1)
        {
            scanf("%d", &A[i]);
            sum += A[i];
        }
        M = sum / n;
        C[0] = 0;
        for (int i = 1; i < n; ++i )
            C[i] = C[i-1] + A[i] - M;
        sort(C, C+n);
        int p = (n+1)>>1;
        ans = 0;
        rep(i, n, 1)
            ans += labs( C[p] - C[i]);
        cout << ans << endl;
    }
    return 0;
}

Problem:Central Avenue Road 感觉第一次写的比较清晰的判断点线关系。

题意:给出一些整数点,然后求某两点的连线,使得左右两边的点数量相差小于1并且要求这条线最短。

根据y = kx + b 推 ax + by + c = 0,这两个直线方程,进而带入点判断正负来判断在直线的上/下(左/右)。

# include <cstdio>
# include <algorithm>
# include <cmath>
# define rep(x) for ( x = 1; x <= n; ++x )
using namespace std;
int x[107], y[107];
int n;

double ans;
inline double sqr(double x)
{ return x * x; }
inline double dis(int a, int b)
{
    double sum = sqr((double)(x[a] - x[b])) + sqr((double)(y[a] - y[b]));
    return sqrt(sum);
}
void test(int a, int b)
{
    int i, j;
    int l = 0, r = 0;
    double K, B;
    if ( x[a] == x[b] && y[a] == y[b] ) return;
    if ( x[a] == x[b] )
    {
        rep(i)
            if ( x[i] < x[a] ) l++;
            else if ( x[i] > x[a] ) r++;
    }
    else if ( y[a] == y[b] )
    {
        rep(i)
            if ( y[i] < y[a] ) l++;
            else if ( y[i] > y[a] ) r++;
    }
    else
    {
        K = (y[a] - y[b]) / ( x[a] - x[b] );
        B = y[a] - K * x[a];
        rep(i)
            if ( i == a || i == b ) continue;
            else
            {
                double tmp = K * x[i] - y[i] + B;
                if ( tmp > 0 ) l++;
                else if ( tmp < 0) r++;
            }
    }
    if ( abs(l-r) < 2 )
    {
        double d = dis(a, b);
        if ( d < ans ) ans = d;
    }
}
int main()
{
    int i, j;
    while ( scanf("%d", &n), n )
    {
        ans = 9999999.99;
        rep(i) scanf("%d%d", &x[i], &y[i]);
        rep(i)
            for ( j = i + 1; j <= n; ++j )
                test(i, j);
        if ( n == 1 ) ans = 0.0;
        printf("%.3f\n", ans);
    }
    return 0;
}

ACM 山东第一届省赛 Problem F Fairy tale

模拟题。读懂题目,跟着题目模拟就可以了。需要注意的就是geometrical
distance是两点间的直线距离,我理解为行和列差值的和错了一次。

有趣的是此题数据只有找到宝藏的情况。

但是,为什么不再问问能不能出来呢。

# include <cstdio>
# include <cstring>
# define rep(x) for ( x = 1; x <= n; ++x )
const int go[4][2] = {0,1, 0,-1, 1,0, -1,0}; //E,W,S,N
const int pgo[4][2] = {0,1, 0,-1, -1,0, 1,0}; //E,W,N,S prefer!

const int inf = 2147483647;
int n;
int m[35][35];
bool circle;

int x, y, tx, ty, time;

int abs(int x) { return x > 0 ? x: -x; }
int sqr(int x) { return x * x; }

bool inside(int x, int y)
{ return (x > 0 && x <= n && y > 0 && y <= n); }

bool better(int x, int y, int xx,int yy)
{ return sqr(x-tx)+sqr(y-ty) < sqr(xx-tx)+sqr(yy-ty); }

void Init()
{
    int i,j,k,l,p;
    char s[35];
    rep(i)
    {
        scanf("%s", s+1);
        rep(j)
            if ( s[j] == 'E' ) m[i][j] = 0;
            else if ( s[j] == 'W' ) m[i][j] = 1;
            else if ( s[j] == 'S' ) m[i][j] = 2;
            else if ( s[j] == 'N' ) m[i][j] = 3;
    }
    x = y = 1;
    tx = ty = n;
    time = 0;
    circle = false;
}

int cnt = 0;
int sec[103][5];
bool check()
{
    int i;
    for ( i = 1; i < time; ++i )
        if ( sec[i][0] == x && sec[i][1] == y && sec[i][2] == tx && sec[i][3] == ty && sec[i][4] == (time&3) )
        {
            circle = true;
            return true;
        }
    sec[i][0] = x,sec[i][1] = y,sec[i][2]= tx, sec[i][3] = ty, sec[i][4]= (time&3);
    return false;
}

bool step()
{
    int f1 = (m[x][y]+time)&3, f2 = (m[tx][ty]+time)&3;
    if ( x == tx && y == ty ) return true;
    if ( check() ) return false;
    int xx = x + go[f1][0];
    int yy = y + go[f1][1];
    if ( inside(xx,yy) ) x = xx, y = yy; //自动走
    xx = x, yy = y;
    for ( int i = 0; i < 4; ++i ) //喜好行走
    {
        if ( !inside(x + pgo[i][0], y + pgo[i][1]) ) continue;
        if ( better(x + pgo[i][0],y + pgo[i][1], xx, yy) )
            xx = x + pgo[i][0], yy = y + pgo[i][1];
    }
    x = xx, y = yy;
    int txx = tx + go[f2][0];
    int tyy = ty + go[f2][1];
    if ( inside(txx, tyy) ) tx = txx, ty = tyy; //宝藏走
    ++time;
    return false;
}

int main()
{
    int t = 0;
    while ( scanf("%d", &n), n )
    {
        if ( ++t > 1 ) printf("\n");
        Init();
        printf("Case %d:\n", t);
        while ( time < 100 )
        {
            if ( step() )
            {
                printf("Get the treasure! At step %d.\n", time);
                break;
            }
            if ( circle )
            {
                printf("Impossible. At step %d.\n", time);
                break;
            }
            if ( time == 99 ) printf("Not sure.\n");
        }
    }
    return 0;
}

SPOJ QTREE 树链剖分 做法。

这个做法。。。纯粹。。。。恶心自己。。。。泪奔。。。。

# include <cstdio>
# include <cstring>
# include <iostream>
# include <algorithm>
# include <vector>
using namespace std;
const int SIZE = 10007;
const int INF = 2147483647;
int t, n;
struct EDGE { int v, next, len; } e[SIZE<<1];
int h[SIZE], tot, Edgesign[SIZE];
void AddEdge(int u, int v, int len)
{
	e[++tot].v = v;
	e[tot].len = len;
	e[tot].next = h[u];
	h[u] = tot;
}
bool vis[SIZE];
int tree[(SIZE<<1)<<2], pos[(SIZE<<1)<<2], M;
int list[SIZE<<1], firstshow[SIZE], ltot;
int tsize[SIZE], Ednode[SIZE], dist[SIZE];
int Generfa(int s, int t)
{
	s = firstshow[s];
	t = firstshow[t];
	if ( s > t ) swap(s, t);
	int ans = 0, min = INF;
	for ( s+=M-1, t+=M+1; s^t^1; s>>=1, t>>=1)
	{
		if (~s&1 && tree[s^1] < min) min = tree[s^1], ans = pos[s^1];
		if ( t&1 && tree[t^1] < min) min = tree[t^1], ans = pos[t^1];
	}
	return list[ans];
}
inline void Intree(int &x, int &d)
{
	tree[M + (++ltot)] = d;
	list[ltot] = x;
	pos[M+ltot] = ltot;
	if ( firstshow[x] == 0 ) firstshow[x] = ltot;
}
void LCA(int x, int d)
{
	vis[x] = true;
	Intree(x, d);
	tsize[x] = 1;
	dist[x] = d;
	for (int i = h[x]; i; i = e[i].next)
	{
		if ( vis[e[i].v] ) continue;
		LCA(e[i].v, d+1);
		Intree(x, d);
		tsize[x] += tsize[e[i].v];
		Ednode[e[i].v] = e[i].len; //边权加在点上
	}
	vis[x] = false;
}
class Sgttree
{
	public:
	vector <int> tree;
	int size, M;
	void Init(int insize)
	{
		size = insize; tree.clear();
		if ( size == 1 ) tree.resize(7);
		else tree.resize(size<<2);
		for (M = 1; M <= size + 1; M <<= 1);
		for (int i = (size<<2) - 1; i; --i ) tree[i] = -INF;
	}
	void Build()
	{
		for (int i = M - 1; i; --i )
			if ( tree[i<<1] > tree[(i<<1)+1] ) tree[i] = tree[i<<1];
			else tree[i] = tree[(i<<1)+1];
	}
	void Modify(int x, int n)
	{
		tree[n+=M] = x;
		for ( n >>= 1; n; n >>= 1 ) tree[n] = max(tree[n<<1], tree[(n<<1)+1]);
	}
	int getmax(int s, int t)
	{
		int max = -INF;
		if ( s > t ) swap(s, t);
		for ( s+=M-1, t+=M+1; s^t^1; s>>=1, t>>=1 )
		{
			if (~s&1 && tree[s^1] > max ) max = tree[s^1];
			if ( t&1 && tree[t^1] > max ) max = tree[t^1];
		}
		return max;
	}
}_st;
vector <Sgttree> sgtree;
int belong[SIZE], sgttot, sgtsize[SIZE], sgtpos[SIZE], pointpos[SIZE];
int firptr[SIZE]; //当前线段树中第一个节点编号
int fa[SIZE];
void SelcEdge(int x)
{
	vis[x] = true;
	int i, maxsize = -INF, which;
	for (i = h[x]; i; i = e[i].next)
		if ( !vis[e[i].v] && tsize[e[i].v] > maxsize ) maxsize = tsize[e[i].v], which = e[i].v;
	for (i = h[x]; i; i = e[i].next)
	{
		if ( vis[e[i].v] ) continue;
		if ( e[i].v == which )
		{
			if( belong[x] == -1 ) belong[e[i].v] = ++sgttot;
			else belong[e[i].v] = belong[x];
		}
		else belong[e[i].v] = -1; //标记为轻边指向点
		SelcEdge(e[i].v);
	}
	vis[x] = false;
}
inline void InsertSgt(int x)
{
	int k = belong[x];
	if ( sgtpos[k] == 0 ) firptr[k] = x;
	sgtree[k].tree[sgtree[k].M + (++sgtpos[k])] = Ednode[x];
	pointpos[x] = sgtpos[k];
}
void CreatSgt(int x)
{
	vis[x] = true;
	if ( belong[x] != -1 ) InsertSgt(x);
	for (int i = h[x]; i; i = e[i].next )
	{
		if ( vis[e[i].v] ) continue;
		CreatSgt(e[i].v);
		fa[e[i].v] = x;
	}
	vis[x] = false;
}

void Init()
{
	int i, a, b, c;
	scanf("%d", &n);
	memset(h, 0, sizeof(h));
	memset(vis, 0, sizeof(vis));
	memset(firstshow, 0, sizeof(firstshow));
	memset(tree, 44, sizeof(tree));
	memset(sgtsize, 0, sizeof(sgtsize));
	memset(sgtpos, 0, sizeof(sgtpos));
	sgtree.clear();
	tot = ltot = sgttot = 0;
	Ednode[1] = -INF;
	for ( M = 1; M <= (n<<1)+1; M <<= 1 );
	for (i = 1; i != n; ++i )
	{
		scanf("%d%d%d", &a, &b, &c);
		AddEdge(a,b,c); Edgesign[i] = tot;
		AddEdge(b,a,c);
	}
	LCA(1,0);
	for (i = ltot+M; i; --i )
		if ( tree[i>>1] > tree[i] ) tree[i>>1] = tree[i], pos[i>>1] = pos[i];

}
void change(int x, int a)
{
	int b = e[ Edgesign[a]+1].v, k;
	a = e[ Edgesign[a] ].v;
	if( dist[a] < dist[b] ) a = b;
	k = belong[a];
	if ( k != -1 ) sgtree[k].Modify(x, pointpos[a]);
	else Ednode[a] = x;
}
int find(int a, int b)
{
	if ( a == b ) return 0;
	int root = Generfa(a, b), ans = -INF, l, r;
	for (int i = 0; i != 2; ++i, a = b )
		for ( ;a && dist[a] > dist[root]; a = fa[a] )
			if ( belong[root] == belong[a] && belong[a] != -1 )
			{
				l = min(pointpos[root], pointpos[a])+1;
				r = max(pointpos[root], pointpos[a]);
				ans = max( ans, sgtree[belong[root]].getmax(l, r) );
				break;
			}
			else if ( belong[a] == -1 ) ans = max( ans, Ednode[a]);
			else
			{
				ans = max( ans, sgtree[belong[a]].getmax(1,pointpos[a]) );
				a = firptr[belong[a]];
			}
	return ans;
}
void Work()
{
	int i, a, b;
	char tmp[30];
	belong[1] = -1; SelcEdge(1);
	for (i = 1; i <= n; ++i ) if ( belong[i] > 0 ) ++sgtsize[ belong[i] ];
	sgtsize[0] = 1;
	for (i = 0; i <= sgttot; ++i ) //vector下表从0开始
	{
		_st.Init(sgtsize[i]);
		sgtree.push_back(_st);
	}
	fa[1] = 1; CreatSgt(1);
	for (i = 1; i <= sgttot; ++i ) sgtree[i].Build();
	while( true )
	{
		scanf("%s", tmp);
		if ( tmp[0] != 'D' ) scanf("%d%d", &a, &b);
		else return ;
		switch(tmp[0])
		{
			case 'Q': printf("%d\n", find(a, b)); break;
			case 'C': change(b, a); break;
		}
	}
}
int main()
{
	scanf("%d", &t);
	while(t--)
	{
		Init();
		Work();
	}
	return 0;
}

C风格的邻接表模板,链式前向星

今天和某大神交流了下图的存储,咱给出了lk大神教给咱的链式前向星代码,这里贴一下好了。
const int maxn = 100; //最大点数
const int maxm = 500; //最大边数

//边定义
struct Edge
{
	int v, next; //v记录指向的节点,next记录下一条记录的数组下标
	int cost; //记录距离,也可以是其他东西
} e[Maxm];

//需要的其他参数
int h[maxn] = {0}, tot = 0; //h[x] 表示 x 节点的第一条边的数组下标(为0时表示不存在), tot表示总边数

//加单向边
void add(int u, v, c)
{
	e[++tot].v = v;
	//插入到h数据链表的第一位
	e[tot].next = h[a];
	h[u] = tot;

	e[tot].cost = c;
}

//x节点上边的遍历
for (int i = h[x]; i; i = e[i].next )
{
	int v = e[i].v;//v为目标节点
}