您的位置:首页 > 编程语言

Codeforces Beta Round #80 (Div. 2 Only)

2011-08-08 22:58 344 查看

A题,易水之

B题,易水之

C题

题意:判断给定图是否存在一个环,且不在环上点是否是以环上点引出的一个或多个生成树

方法:先判图的连通性,然后去叶子节点(度为1),之后判断剩余点是否构成环(每个点度为2),且构成环点的个数不少于3

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxN = 100 + 2;

#define f(i,s,e) for(int i = s; i <= e; ++i)
#define i(x,y) scanf("%d %d", &x, &y)
#define e(x,y,v) edge[x][y] = v, edge[y][x] = v
#define a(x,y,v) Deg[x] += v, Deg[y] += v
#define n() puts("NO")
#define _n() puts("FHTAGN!")
#define eq(x,y) if(x != y) return 0

static int N, M, u, v;
static bool edge[maxN][maxN];
static int Deg[maxN];
static bool vst[maxN];

int _O_(int u)
{
vst[u] = 1; int ret = 1;
f(j,1,N) if(edge[u][j] && !vst[j]) ret += _O_(j);
return ret;
}

bool _O_O_()
{
f(k,1,N) f(i,1,N) if(Deg[i] == 1) f(j,1,N) if(edge[i][j])
e(i,j,0), e(j,i,0), a(i,j,-1), vst[i] = 0; return 1;
}

bool _O_O_O_()
{
int ret = 0;
f(i,1,N) if(vst[i]) eq(Deg[i],2);
else ++ret; return ret >= 3;
}

int main()
{
//freopen("data.in", "r", stdin);
//freopen("data.out", "w", stdout);
while( i(N, M) == 2 ) {
f(i,1,N) f(j,1,N) e(i,j,0);
f(i,1,N) Deg[i] = 0, vst[i] = 0;
f(i,1,M) i(u,v), e(u,v,1), a(u,v,1);

if( _O_(1) != N ) n();
else if( _O_O_() && _O_O_O_() ) _n();
else n();
}
return 0;
}


D题:未解

E题

题意:给定长度为N的序列 { A(1), A(2), ..., A(N)},有Q个查询,每个查询给2个数值 X 和 Y, 计算 Sum { A(X + K * Y) | X + K * Y <= N },N和Q的范围是 300,000

思想:按照 Y 值从小到大排序,然后去计算每一个查询,当当前查询的Y值和前一个Y值相同,可以O(1)计算出来,否则枚举求解;注意,看起来好像是最暴利的方法,这里有一个处理,那就是对200个需要做单独处理,前200个处理中,如果出现 相邻 Y 值相等,算作一个,以下给出具体的分析

我们假设 N 取最大值即300,000,Q 也取得最大值300,000,这样,粗略的看,一次查询如果是最笨的穷举,那么最坏会是X = 1,Y=1,这样需要计算一次复杂度是多少呢?

答:是 K = (N - X ) / Y = (300,000 - 1) / 1 = 299,999 === 300,000

这样看起来好像总的复杂度是 Q*K=300,000 * 300,000 = 90,000,000,000,铁定超时?(时限是4S)

你被表面的现象迷惑了,现在我们一步一步来处理这个问题

问题一:得到输入的数据后,按照 Y 值排序,需要做怎样的处理

答:需要存储这个查询的原始输入 ID 号,这个大家都懂,不多说了

问题二:如果枚举某一个 X 和 Y 情况的值,怎么算?

答:我们建立一个临时的求和数组,Sum[ maxN ],其中 maxN = 300,000

我们这样处理

首先,memcpy(Sum, A, sizeof(A));

然后,i 从 1 枚举到 N, 当 i > Y 时, Sum[ i ] = Sum[ i ] + Sum[ i - Y ];

定义:maxK = (N - X)/ Y,表示前述 K 取得的最大值,那么最后的结果是不是就是 Sum[ X + K * Y ]呢?

因为容易知道

Sum[ X + K * Y] = A[ X + K * Y ] + Sum[ X + (K - 1) * Y ] = ... = A[ X + K * Y ] + A[ X + (K - 1) * Y ] + ... + A[ X + Y ] + Sum[ X ] = A[ X + K * Y ] + A[ X + (K - 1) * Y ] + ... + A[ X + Y ] + A[ X ]

所以,你轻松的认为就是它了,是的,一般的就可以这么认为了,但是这里有一个小bug,那就是上式成立的默认条件是 X < Y

如果 X > Y, 那么 显然还有 Sum[ X ] = A[ X ] + Sum[ X - Y ],而这时候的 Sum[ X - Y ] 并不是我们需要的,所以这里我们需要特别判断处理了

问题三:如果当前的Y值和前一个Y值是相等的,那么怎么处理呢?(注意,已经按Y值排序,所以出现 Y值相等是可能的)

答:再看看问题二,我们从知道,当计算当前的查询的时候,前一个查询计算遗留了很多宝贵的财富,如 Sum 数组值,我们要好好利用这个 Sum 值

容易知道,如果这次也枚举的话,那么显然得到的 Sum 数组值是和上一个是一模一样的,没有任何改变,只不过这时候我们需要的是确定 Sum[ * ]中 * 的值,结合前述

我们知道,就是老套路,只不过省略了求 Sum 数组的阶段了

问题四:解释什么是 “枚举前200个”

答:假设我们定义三元组(Xi, Yi, IDi)表示排序后的第 i 个存储的查询,这个查询在原始输入的时候,是排在 IDi 位的,注意,我们知道,是按照 Y 值排序的,所以,Y 值是非递减的 ,所以我们这里说的 “枚举前200个” 的这个 '200" 就是针对Y值而言的,也就是说,我们对前200个Y值采取枚举处理,注意,Y值相同,算1个,因为从前面知道,当当前的Y值和前一个Y值是相同的时候,那么计算当前的值时,完全可以O(1)得到答案。

问题五:“枚举前200个”的复杂度是多少呢?

我们知道,前200是针对Y值得,所以,处理的Y值是不同的,我们有知道,Y值肯定是越小算的次数越多?是吗,肯定是的

这样,我们不妨取最坏情况,假设前200个值是1,2,3,...,200。

好,那么 X 值怎么办呢?显然,X值肯定也是越小越好(自己想想),但是X值可不是离散有序的,所以我们假设是最坏情况,即假设前200个有 X = 1

那么,问题就明朗了,在这种最坏的情况下,需要多大的复杂度呢?

容易计算通项公式:T(i) = (N - Xi) / Yi,所以有

第1个:(N - 1) / 1

第2个:(N - 1)/  2

第3个:(N - 1)/  3 

...

第200个:(N - 1)/ 200

所以总的时间复杂度是:T = (N - 1)* (1 + 1 / 2 + 1 / 3 + 1 / 4 + ... + 1 / 200) = 299,999 * 《*》

编程计算《*》约为 5.878030948,所以可以计算 T 的值约为 1763403,可以初步估计是 2000000,大约是 10 的 6 次方数量级

换句话说,枚举计算前200个的时间复杂度大约是 10^6 数量级,显然,这是可以接受的

问题六: 200个以后的怎么处理呢?

答:这个问题问得好,这里采取的方式就是赤裸裸的枚举了,如下所示

ans = 0;
while( X <= N ) ans += A[X], X += Y;

问题七:不用我说,你都知道,这时候的200个以后的复杂度又是多少呢?

答:这时候我们才去的措施仍然是最坏计算,我们假定前200个不存在 Y 值相同的,而200个以后的全是相同的,且值为 201,“这是为什么呢?”

因为 Y 值越小越好,故此

同样的,我们假定所有的 X 值均为1,“这是为什么呢?” ,理由同上。

好了,现在计算总的复杂度:

第201个:(300000  - 1) / 201

第202个:(300000  - 1) / 201

...

第300000个:(300000 - 1)/ 201

所以,T = (300000 - 1)* { (300000 - 201 + 1)/ 201 },计算其值约为 447461195,即约为 4.5 * 10^8,差不多是 “4.5亿”

这样的复杂度,计算机也是可以承受的

总结:所以,这样一来,总的时间复杂度大约为 4.5亿左右(450,000,000),计算机是扛的住的,何况这只是最坏的情况,数据有随机生成,就更快了。

所以,有时候,粗略的直觉未必可信,计算之后,才发现科学才是硬道理。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxN = 3 * 100000 + 2;

struct node
{
int s, t, idx;

inline void in() { scanf("%d %d", &s, &t); }
inline bool operator<(const node &tmp) const { return t < tmp.t; }
};

static int nQ; // total Queries
static node Q[maxN]; // i know you have known

static int nA; // total Numbers
static int A[maxN]; //  like before

typedef long long i64d;

static i64d ans[maxN]; // note answers
static i64d sum[maxN]; // sum[x + k*y] = A[x + k*y] + sum[x + (k-1)*y]

int main()
{
//freopen("data.in", "r", stdin);
//freopen("data.out", "w", stdout);
while( scanf("%d", &nA) == 1 ) {
for(int i = 1; i <= nA; ++i) scanf("%d", A + i);
scanf("%d", &nQ);
for(int i = 0; i < nQ; ++i) Q[i].idx = i, Q[i].in();

sort(Q, Q + nQ);

static int nT, pre_t; nT = pre_t = 0;
for(int i = 0, j; i < nQ; ++i) {
if( Q[i].t != pre_t && nT <= 200 ) {
++nT, pre_t  = Q[i].t;
for(j = 1; j <= nA; ++j) {
sum[j] = A[j];

if( j > Q[i].t ) sum[j] += sum[j - Q[i].t];
}
}

if( Q[i].t == pre_t ) {
ans[ Q[i].idx ] = sum[ Q[i].s + (nA - Q[i].s) / Q[i].t * Q[i].t ];
if( Q[i].s > Q[i].t ) ans[ Q[i].idx ] -= sum[ Q[i].s - Q[i].t ];
} else {
ans[ Q[i].idx ] = 0LL;
for(j = Q[i].s; j <= nA; j += Q[i].t) ans[ Q[i].idx ] += A[ j ];
}

}

for(int i = 0; i < nQ; ++i) printf("%I64d\n", ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  存储 struct 编程 c