牛客练习赛27

先占个坑

题目链接

mdzz"愤怒"那道题考完之后才做出来有些遗憾啊(琛哥和雷哥好像还是听不懂)

切4道/rank45

反思...D题考场上没成功想出来...第二天早上捋了捋思路就A了...

A-纸牌

小w想和你van纸牌 小w有两张纸牌,两张纸牌上都有相同的正整数n 每一轮一张纸牌上的数都可以减去小于等于另外一张纸牌上的数的数 每一轮只能操作和上轮不同的纸牌 小w想知道三轮之后两纸牌上数字之和的最小值 注意,不能减为负数

答案是\(\displaystyle \left\lceil\frac{n}{2}\right\rceil\),不是很会推。这里放上官方题解吧。

最优解先手一定变为了0,一定有一或者两次先手共减少了n,设先手减少的为x 设k为中间后手减少的那次,k要满足\(k\le n-x,n-x\le n-k\) 此时\(k\le x\)\(k\le n-x\) 最大化\(k\)\(\displaystyle \left\lfloor\frac{n}{2}\right\rfloor\)所以答案为\(\displaystyle2 *n-(n+\left\lfloor\frac{n}{2}\right\rfloor)\) ,也就是 \(\displaystyle \left\lceil\frac{n}{2}\right\rceil\)

我xjb说下我的推法,反正就是各种设变量,xjb找就行了,好了说完了

代码不放了

B-手办

小w想拉大w入坑水团,小w认为这样大w就可以多9个手办了 虹团要出道了,a团,b团,c团呢 \(\varphi's\)出道之后,小w有了n个手办 她对每个手办进行了编号,编号为1 ~ n 她对编号为k的手办的好感度为(a * b)整除k的有序整数对(a, b)的个数(1≤ a, b ≤ k) 现在小w成为老年人,厨力不够了,她想知道,她对所有手办的好感度之和对2333取模之后的数是多少 \(n\le10^{12}\)

不会做

C-水图

小w不会离散数学,所以她van的图论游戏是送分的 小w有一张n个点n-1条边的无向联通图,每个点编号为1~n,每条边都有一个长度 小w现在在点x上 她想知道从点x出发经过每个点至少一次,最少需要走多少路

从x出发找最长路(搜索),用所有边权和2倍减去这个数就是答案(因为最多有从原点出的一条链只被遍历一次,所以我们要让那个边最长)

代码太漂亮太丑不想放

D-愤怒

小w很生气 小w有一个长为n的括号序列愤怒小w想把这个括号序列分为两个括号序列 小w想让分为的这两个括号序列同时合法 小w想知道一共有多少种划分方案 (划分的意思是划分为两个子序列) 注意两个序列是 A,B 和 两个序列是B,A 算两种方案,也就是同一位置位于不同划分为方案不同\(n\le10000\)

我在nowcoder做到的第三道括弧匹配题

f[i][x1][x2]表示到第i个位置,两个括弧深度为x1和x2的方案数,容易发现的是对于某一确定的位置,括弧深度是已知的,所以两个子序列的括弧深度,知道一个另一个就确定了,所以第三维可以省掉。第一维由于只和上一次结果有关,所以第一维可以省掉,时间复杂度N^2能卡过

#include <bits/stdc++.h>
using namespace std;

int N, a[10010], f[2][10010];
char str[10010];

int main()
{
	scanf("%d%s", &N, str + 1);
	for (int i = 1; i <= N; i++)
	{
		if (str[i] == '(')
			a[i] = 1;
		if (str[i] == ')')
			a[i] = -1;
		a[i] += a[i - 1];
	}
	f[0][0] = 1;
	for (int i = 1, t = 1; t <= N; t++, i ^= 1)
	{
		for (int x1 = 0; x1 <= a[t]; x1++)
		{
			int x2 = a[t] - x1;
			if (str[t] == '(')
			{
//				f[i][x1][x2] = 0;
//				if (x1 >= 1)
//					f[i][x1][x2] += f[i - 1][x1 - 1][x2];
//				if (x2 >= 1)
//					f[i][x1][x2] += f[i - 1][x1][x2 - 1];
				f[i][x1] = 0;
				if (x1 >= 1)
					f[i][x1] += f[i ^ 1][x1 - 1];
				if (x2 >= 1)
					f[i][x1] += f[i ^ 1][x1];
			}
			if (str[t] == ')')
			{
//				f[i][x1][x2] = 0;
//				f[i][x1][x2] += f[i - 1][x1 + 1][x2];
//				f[i][x1][x2] += f[i - 1][x1][x2 + 1];
				f[i][x1] = f[i ^ 1][x1 + 1] + f[i ^ 1][x1];
			}
			f[i][x1] %= 2333;
//			printf("f[%d][%d][%d] = %d\n", t, x1, x2, f[i][x1]);
		}
		for (int fk = a[t] + 1; fk <= N; fk++)
			f[i][fk] = 0;
	}
	printf("%d\n", f[N & 1][0]);
	return 0;
}

E-欧拉

小w是欧拉的粉丝 众所周知 \(\displaystyle \varphi(n)=\sum_{d|n}d\mu\left(\frac n d\right)\)\(\varphi\)为欧拉函数,\(\mu\)为莫比乌斯函数,现在小w想求这东西

\(\displaystyle F(n)=\sum_{d|n}d^k\mu\left(\frac n d\right)\),有m次询问\(m \le 500000\),\(n\le5000000\)\(1\le k\le100\),每次询问\(F(n)\mod998244353\)

首先\(F(n)=\mathbf{id^k}\times\mu\),一定是积性函数,所以线性筛一下,当\(n\)时质数,手酸一下发现\(F(n)=n^k-1\)

如果\(\gcd(b,p)\neq1\),那么\(F(bp)=p^kF(b)\),就可以线性筛了

#include <bits/stdc++.h>
#define maxn 5000010
#define p 998244353
using namespace std;
 
bool vis[maxn];
int prime[maxn], tot, k;
long long f[maxn];
 
long long qpow(long long x, long long y)
{
    long long res = 1;
//  printf("qpow %lld %lld\n", x, y);
    while (y > 0)
    {
        if (y & 1)
            res = res * x % p;
        x = x * x % p;
        y >>= 1;
    }
//  printf("qpow res = %lld\n", res);
    return res;
}
 
void prework()
{
    f[1] = 1;
    for (int i = 2; i <= 5000000; i++)
    {
        if (vis[i] == 0)
        {
            prime[++tot] = i;
            f[i] = (qpow(i, k) + p - 1) % p;
        }
        for (int j = 1; j <= tot && prime[j] * i <= 5000000; j++)
        {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0)
            {
                f[i * prime[j]] = f[i] * (f[prime[j]] + 1) % p;
                break;
            }
            else
            {
                f[i * prime[j]] = f[i] * f[prime[j]] % p;
            }
        }
//      printf("f[%d] = %lld\n", i, f[i]);
    }
}
 
 
void read(int &x)
{
    static char ch;
    x = 0;
    ch = getchar();
    while (!isdigit(ch))
        ch = getchar();
    while (isdigit(ch))
    {
        x = x * 10 + ch - 48;
        ch = getchar();
    }
}
 
int main()
{
    int T, x;
    read(T);
    read(k);
    prework();
    for (int i = 1; i <= T; i++)
    {
        read(x);
        printf("%lld\n", f[x]);
    }
    return 0;
}

F-计数

题目是假的 小w喜欢Van数列 小w有一个长度为n的由3和7构成的环状序列 小w规定序列中任意相邻的m个数,3的个数不能超过7的个数 小w想知道这样的序列共有多少种 ,方案数对于998244353取mod \(n\le10^{12},m\le5\)

qtmd为什么我写的矩阵乘法总是四层循环

#include <bits/stdc++.h>
#define p 998244353
#define cnt cnm
#define int long long
using namespace std;



struct fuck
{
	int f[32][32];
};

long long n;
int m, cnm;

bool check(int x)
{
	int cnt0 = 0, cnt1 = 0;
	for (int i = 1; i <= m; i++)
//	while (x > 0)
	{
		if (x & 1)
			cnt1++;
		else
			cnt0++;
		x >>= 1;
	}
	return cnt0 <= cnt1;
}

bool val(int x, int y)
{
	int pp = (x << m) + y;
	while (pp > cnm)
	{
		if (check(pp & cnm) == false)
			return false;
		pp >>= 1;
	}
	return true;
}
bool valid[32][32];

fuck operator*(const fuck &a, const fuck &b)
{
	fuck res;
	memset(res.f, 0, sizeof(res.f));
	for (int l = 0; l <= cnm; l++)
		for (int r = 0; r <= cnm; r++)
			for (int ml = 0; ml <= cnm; ml++)
				for (int mr = 0; mr <= cnm; mr++)
				{
					if (valid[ml][mr])
						(res.f[l][r] += 1LL * a.f[l][ml] * b.f[mr][r] % p) %= p;
				}
	return res;
}

fuck qpow(fuck x, long long y)
{
	fuck res;
	memset(res.f, 0, sizeof(res.f));
	for (int i = 0; i <= cnt; i++)
		if (check(i) == true)
			res.f[i][i] = true;
	while (y > 0)
	{
		if (y & 1)
			res = res * x;
		x = x * x;
		y >>= 1;
	}
	return res;
}

void cat(fuck &a)
{
	fuck res;
	memset(res.f, 0, sizeof(res.f));
	for (int i = 0; i <= cnm; i++)
	{
		for (int j = 0; j <= 1; j++)
		{
			int x = ((i << 1) | j) & cnm;
			if (check(x))
			{
				for (int src = 0; src <= cnm; src++)
				{
					(res.f[src][x] += a.f[src][i]) %= p;
				}
			}
		}
	}
	a = res;
}

signed main()
{
	scanf("%lld%lld", &n, &m);
	cnm = (1 << m) - 1;
	fuck fucker;
	for (int i = 0; i <= cnt; i++)
		for (int j = 0; j <= cnt; j++)
			if (val(i, j))
				valid[i][j] = true;
	memset(fucker.f, 0, sizeof(fucker.f));
	for (int i = 0; i <= cnt; i++)
		if (check(i) == true)
			fucker.f[i][i] = true;
	int mian = n / m, rest = n % m;
	fuck nmr = qpow(fucker, mian - 1);
	while (rest > 0)
	{
		cat(nmr);
		rest--;
	}
	int tot = 0;
	for (int tail = 0; tail <= cnm; tail++)
		for (int head = 0; head <= cnm; head++)
		{
			if (valid[tail][head])
			{
//				printf("%d--%d\n", head, tail);
				(tot += nmr.f[head][tail]) %= p;
			}
		}
	cout << tot << endl;
	return 0;
}
posted @ 2018-09-22 21:50  ghj1222  阅读(541)  评论(0编辑  收藏  举报