GDUFS 2019信息学院程序设计新手赛(正式赛)题解

题目连接 :https://oj.lpoj.cn/contest/56

A lpoj is the best

这题是送分题,主要考察转义字符

#include
using namespace std;
int main(){
  
  int n;
  cin>>n;
  while(n--){
  cout<<"\"www.lpoj.cn is the best!\""<

B 偶数求和

考察int的范围,这题要用long long int长整型才能保存答案

#include
using namespace std;

int main()
{
	int n,i;
	long long int tmp,s=0;
	cin >>n;
	for (i=0;i>tmp;
		if (tmp%2==0) { s+=tmp; }
	}
	cout <

C 出题人的良心

主要考察大家的读题和细心能力。这道题用printf的话,会简单很多,少很多逻辑。

#include 
int main()
{
	int a,b;
	float c;
	scanf("%d %d",&a,&b);
	c=(float)a/b;
	if(b>0)
	printf("%d/%d=%.2f\n",a,b,c);
	if(b<0)
	printf("%d/(%d)=%.2f\n",a,b,c);
	if(b==0)
	printf("%d/%d=Error\n",a,b);
	return 0;	
}

D cat & grep

这题是出题人拍脑袋想出来的。一个for循环就好了。

#include
using namespace std;

int n;
char s[1100];

int main(){
    while(~scanf("%s", s)){
        n = strlen(s);
        int ans = 0;
        for(int i=3; i

E ka ge bu n shi n no jyu tsu

简单思维题,数字优先拆3,接着拆2,至于为什么,因为要使乘积最大,肯定是越平均越好,直到3,因为3不能拆了。

#include
using namespace std;

int n, m;

bool ok(){
    if(n >= 60) return 1;
    long long s = 1;
    while(n > 4){
        s = s * 3;
        n -= 3;
        if(s >= m)  return 1;
    }
    return s * n >= m;
}

int main(){
    while(~scanf("%d %d", &n, &m)){
        puts(ok()?"Yes":"No");
    }
    return 0;
}

F 好快的刀

做法就是因为数据范围很小,所以我们可以枚举所有直线,然后计算所有圆到直线的距离即可。但是这里有很多特殊情况要考虑。比如 斜率不存在的情况等等。

#include 
#include 
using namespace std;
const double eps=1e-8;
const double inf =1e20;
//const double pi=acos(-1.0);
int sgn(double x){
    if(fabs(x)>n;
    for(int i=0;i

G 沙雕

GDUFS 2019信息学院程序设计新手赛(正式赛)题解_第1张图片

#include 
using namespace std;
const int mod=1e9+7;
int n,m,w,h; 
int p1,p2;
int main(){
	cin >> n >> m >> w >> h;
	for (int i=w+2;i<=n;i++)
		p1=(p1+1ll*(i-w-1)*(n-i+1)%mod)%mod;
	for (int i=h+2;i<=m;i++)
		p2=(p2+1ll*(i-h-1)*(m-i+1)%mod)%mod;
	cout << 1ll*p1*p2%mod;
}

H 完美子矩阵

首先因为行内可以随意变换,所以只要直接统计0-9数字的每一行是否符合回文的条件。然后,N * M就压缩成N * 10的东西了。因为要满足行回文,因此只要每一行0-9对应的数量一样,并且每一行都合法就是一个回文子矩阵。因为,问题就转化为给你一个序列,问你回文的子串有多少个。
O(N^3)做法:O( n^2)枚举左右端点,暴力O(n)检测,这个明显时间过不了
O(N^2)做法:O(n)枚举回文的对称点,O(n)检测该回文对称的最长半径
O(N)做法:直接用Manacher算法

#include 

using namespace std;
int n, m;
vector num[2005];
bool ok[2005];

bool check1(int r)
{
    int sum = 0;
    for (auto &&i:num[r])
        if (i & 1)
            ++sum;
    return sum < 2;
}

int main()
{
    string t;
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
        num[i] = vector(10);
    for (int i = 0; i < n; i++)
    {
        cin >> t;
        for (auto &&j:t)
            ++num[i][j - 0x30];
        ok[i] = check1(i);
    }
    int l, r, ans = 0;
    for (int i = 0; i < n; ++i)
    {
        l = r = i;
        while (l >= 0 && r < n)
        {
            if (ok[l] && ok[r] && num[l] == num[r])
                ++ans;
            else
                break;

            --l, ++r;
        }
        l = i, r = i + 1;
        while (l >= 0 && r < n)
        {
            if (ok[l] && ok[r] && num[l] == num[r])
                ++ans;
            else
                break;
            --l, ++r;
        }
    }
    cout << ans;
    return 0;
}

I 斐波那契通项

暴力:可以发现这个通项是两项线性递推的公式,double暴力算出前5项,暴力枚举两项系数即可,也可以解二元一次方程组。
科学做法:用特征根来求解,可以百度特征方程求解斐波那契通项

#include
using namespace std;

typedef long long LL;

const int N = 1e5 + 10;
int n, mod;
LL a, b, F[N];

int main(){
    scanf("%lld %lld %d %d", &a, &b, &n, &mod);
    LL A = (a * 2) % mod;
    LL B = ((b - a * a) % mod + mod) % mod;
    F[0] = 2;
    F[1] = A;
    for(int i=2; i<=n; i++){
        F[i] = (F[i-1] * A + F[i-2] * B) % mod;
    }
    printf("%lld\n", F[n]);
    return 0;
}

J 摇摇摇

概率动态规划,令fi表示在i时走到1的概率期望,其中f1=0,通过解方程,f2=f3=f4=f5=f6=6,接下来就是一个线性递推fi = 1/6*(fi-1+fi-2+fi-3+fi-4+fi-5+fi*6)+1。

#include
using namespace std;

const int N = 1e5 + 10;

int n;
double dp[N];

int main(){
    dp[1] = 0.0;
    for(int i=2; i<=6; i++){
        dp[i] = 6.0;
    }
    for(int i=7; i

K Hongrock的柠檬树

引入某位集训队大佬的题解:

输入一棵树,求树上所有两点之间路径或之和。

权神设计出来是用并查集做的。

然而第一反应就是向上合并维护二进制位。写了一发维护1的个数愉快的wa了,之后就忘了有这题了。最近这题被大师兄秒掉了,被教育了一手可以先当成整棵树全是1来做,然后再合并0的个数,减掉0的情况。

瞬间好写了很多。。。。

我们把子树0的个数向上合并的时候,检查一下父节点这一位是否是0,如果是0才合并,因为父节点是所以子节点的必经之路,如果父节点是1,那么子节点经过父节点往上或出来这一位就是1了。

#include

using namespace std;

const int maxn = 1e5 + 5;
typedef long long ll;

ll ans;
int n, val[maxn];
int p[20], sz[maxn][20];
vector G[maxn];


void crack(int x) {
    for (int i = 0; i < 20; ++i) {
        sz[x][i] = !(val[x] >> i & 1);
    }
}

void dfs(int x, int fa) {
    crack(x);

    for (auto v:G[x]) {
        if (v == fa) {
            continue;
        }
        dfs(v, x);
        for (int i = 0; i < 20; ++i) {
            ans -= 1LL * p[i] * sz[x][i] * sz[v][i];
            if (!(val[x] >> i & 1)) {
                sz[x][i] += sz[v][i];
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> val[i];
    }

    int u, v;
    for (int i = 1; i < n; ++i) {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    p[0] = 1;
    for (int i = 1; i < 20; ++i) {
        p[i] = p[i - 1] << 1;
    }

    for (int i = 0; i < 20; ++i) {
        ans += 1LL * p[i] * n * (n - 1) / 2;
    }

    dfs(1, 0);
    cout << ans;
    return 0;
}

L Alice and Bob

初始化部分,对两棵树分别做深搜,维护好LCA(最近公共祖先)所需信息,查一棵树上任意两点间的距离。
看询问,虽然说是两个人的相遇问题,但由于也是两个人的距离之和,所以可以看成一个点到另一个点的最短距离。
传送门只有M个,那么在每个询问中,我们只需要关注这M对点的路径。
比赛时有同学已经做到了LCA了,只是他是通过枚举M对点到两个点的距离之和求最小值,也就是说,在这个策略里,传送门只利用一次。
但是实际上,传送门可以多次利用,在两棵树之间来回跳来求出更短的距离,所以这里的子问题应该是,起点到这2M个点的最短路径,跑个单源最短路算法即可。

#include
using namespace std;

const int N = 2e5 + 10;
const int L = 20;
const int M = 8;
typedef long long LL;
typedef pair pii;
const LL inf = 1e18;

#define pb push_back
#define mp make_pair

int n, Q, m, a[M], dep[N], fa[N][L];
LL dist[N];
vector V[N];

void dfs(int x){
	for(int i=0; i A.dis;
	}
};

int lca(int u, int v){
	if(dep[u] > dep[v])	swap(u, v);
	for(int i=L-1; i>=0; i--){
		if(dep[v] - dep[u] >> i & 1){
			v = fa[v][i];
		}
	}
	if(u == v)	return u;
	for(int i=L-1; i>=0; i--){
		if(fa[u][i] != fa[v][i]){
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}

LL cal(int x, int y){
	int u = lca(x, y);
	return dist[x] + dist[y] - dist[u] * 2;
}

LL solve(int x, int y, int m){
	LL ans = inf;

	priority_queue Q;
	Node nd;
	LL g;

	for(int i=0; i= ans)	break;

		ans = min(ans, nd.dis + cal(a[nd.id]+n, y));

		done[nd.id] = 1;
		
		for(int i=0; i nd.dis + g){
				d[i] = nd.dis + g;
				Q.push(Node(i, d[i]));
			}
		}
	}
	
	return ans;
}

int main(){
	int x, y, z, m;

	scanf("%d", &n);
	
	for(int i=1; i

你可能感兴趣的:(————ACM相关————,集训队相关)