2017长乐国庆欢乐赛Day1

8 篇文章 0 订阅
5 篇文章 0 订阅

原谅排版不好。。。。。。。。。。


第一题(a)

【题目描述】
有 n 头牛,每头牛有个喝水时间,这段时间它将独. 占. 一个 Stall。现在给出每头牛
的喝水时间段,问至少要多少个Stall 才能满足它们的要求。
【输入格式】
从文件a.in 中读入数据。
第一行一个正整数n。
接下来n 行每行两个正整数a; b,表示每头牛的喝水时间段。
【输出格式】
输出到文件a.out 中。
一行一个整数,表示最少要安排多少个Stall 才能满足所有牛的需求。
【样例1 输入】
3
1 2
2 3
3 4
【样例1 输出】
2
【子任务】

对于100% 的数据,1 n 50000; 1 <= a <= b <= 106。


考虑被喝水时间段覆盖次数最多的点的覆盖次数即为答案

差分即可,线段树也可


代码:

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

typedef long long LL;

const int INF = 2147483647;
const int maxseg = 4 * 1000100;
const int maxn = 50100;

int L,R,n;
int maxx[maxseg],set[maxseg];

inline void maintain(int o)
{
	int lc = o * 2,rc = o * 2 + 1;
	maxx[o] = max(maxx[lc],maxx[rc]);
}

inline void increase(int o,int x)
{
	set[o] += x; maxx[o] += x; 
}

inline void pushdown(int o)
{
	if (set[o])
	{
		int lc = o * 2,rc = o * 2 + 1;
		increase(lc,set[o]);
		increase(rc,set[o]);
		set[o] = 0;
	}
}

inline void add(int o,int l,int r,int al,int ar)
{
	if (al <= l && r <= ar)
	{
		increase(o,1);
		return;
	}
	pushdown(o);
	int mid = l + r >> 1,lc = o * 2,rc = o * 2 + 1;
	if (ar <= mid) add(lc,l,mid,al,ar);
	if (mid < al) add(rc,mid + 1,r,al,ar);
	if (al <= mid && mid < ar)
	{
		add(lc,l,mid,al,mid);
		add(rc,mid + 1,r,mid + 1,ar);
	}
	maintain(o);
}

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n = getint();
	L = 1; R = 1000000;
	for (int i = 1; i <= n; i++)
	{
		int l = getint(),r = getint();
		add(1,L,R,l,r);
	}
	printf("%d",maxx[1]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}


第二题(b)
【题目描述】
给定一个不小于2 的整数k,按照如下方式生成一个无限长的序列S (下标从0
开始)。
1. 初始时序列只有一个元素S 0 = 0。
2. 对于j = 1; 2; .....; k - 1 分别把当前序列的每个元素都加上j,得到新的k - 1 个
序列。
3. 把新的k - 1 个序列依次接在当前序列后面,得到一个长度为当前序列长度k 倍
的序列。
4. 把这个序列每一项都变成其除以k 之后的余数,并把这个序列作为新的当前序
列。
5. 执行无穷次操作2 --> 4。
例如k = 3,每一轮执行之后的序列分别是:
012
012 120 201
012120201 120201012 201012120

例如k = 2,则序列是01101001100101101001011001101001
现在给定正整数L; R,你需要求Σh(i) * S i (L <= i <= R)的值,并输出答案对2^32 取模的结果,其中

h(i) = ⌊ ([i mod 20000116] ^ 2+i+804) / 233⌋。
【输入格式】
从文件b.in 中读入数据。
第一行一个正整数T,表示数据组数。
接下来T 行每行三个正整数k; L; R 表示一组数据,其中k 用于生成序列S ,L; R
意义见所求和式。
【输出格式】
输出到文件b.out 中。
对于每组数据,输出一行一个整数表示
ΣRi
=L h(i) S i 的值对232 取模的结果。
【样例1 输入】
10
2 1 10
3 1 10
4 1 10
5 1 10
2 1001 5005
10 123 456
233 1024 6174
16 10000 20000
20 12345678 23456789
987 2333123456789 2333198765432
【样例1 输出】
15
36
51
66
89026303
599966
84450304
2099970147
3683804069
1796954653
【子任务】
对于100% 的数据,T <= 100; 2 <= k <= 1000; 0 <= L <= R <= 10^16;
ΣR - L <= 10^8。


容易发现

对于一个i来说,他的S(i)为i在k进制下各位数的和

那么只需要从L到R不断+1,用类似高精度的方法维护这个和即可


代码:

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

typedef long long LL;

const int INF = 2147483647;
const LL crz = 1ll << 32;

int n,k,x,y;
LL a[100],tot,cur,l,r,ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void ins()
{
	a[1]++;
	for (int i = 1; i <= tot + 1; i++)
	{
		cur = (cur + 1) % k;
		if (a[i] < k) break;
		a[i] = 0; a[i + 1]++;
	}
	if (a[tot + 1]) tot++;
}

inline LL h(LL i)
{
	return ((i % 20000116ll) * (i % 20000116ll) + i * 1ll + 804ll) / 233ll;
}

int main()
{

	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n = getint();
	for (int i = 1; i <= n; i++)
	{
		k = getint(); l = getint(); r = getint();
		LL p = 1;
		cur = 0; y = 1; tot = 0;
		memset(a,0,sizeof(a));
		LL x = l;
		while (x)
		{
			a[++tot] = x % k;
			cur = (cur + a[tot]) % k;
			x /= k;
		}
		ans = cur * h(l);
		for (LL j = l + 1; j <= r; j++)
		{
			ins(); 
			ans = (ans + cur * h(j)) % crz;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

第三题(c)
【题目描述】
从前有个正整数n。
对于一个正整数对(a; b),如果满足a + b <= n 且a + b 是ab 的因子,则成为神奇
的数对。
求神奇的数对的个数。
【输入格式】
从文件c.in 中读入数据。
一行一个正整数n。
【输出格式】
输出到文件c.out 中。
一行一个整数表示答案,保证不会超过64 位有符号整数类型的范围。
【样例1 输入】
21
【样例1 输出】
11
【子任务】
对于20% 的数据,n <= 1000;
对于40% 的数据,n <= 10^5;
对于60% 的数据,n <= 10^7;
对于80% 的数据,n <= 10^12;
对于100% 的数据,n <= 10^14。


很强的一道数学题。。。。考场上只想到了一半


设gcd(a,b) = d,a = a'd,b = b'd

那么有

a'd + b'd <= n

a'd + b'd | a'b'd ^ 2 

整理一下

(a' + b')d <= n , a' + b' | a'b'd

设k = a' + b'

则kd <= n , k | a'b'd

考虑到 k = a' + b' 又 a'与b'互质

那么根据辗转相除法的原理,k与a'和b'都互质

那么显然k | d,且若k确定的情况下,(a',b')的对数是为phi(k)

设d = kx , 显然当k确定时,d的个数为x

那么x * k ^ 2 <= n , 那么k <= n ^ 0.5

综上,我们只需要从1 到n ^ 0.5循环一遍,把n / k * phi(k)累加起来就可以了


代码:

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

typedef long long LL;

const int INF = 2147483647;
const int maxn = 10001000;

LL n;
LL phi[maxn],prime[maxn],tot;
LL ans;
bool mark[maxn];

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	n = getint();
	int len = sqrt(n);
	for (int i = 2; i <= len; i++)
	{
		if (!mark[i])
		{
			prime[++tot] = i;
			phi[i] = i - 1;
		}
		for (int j = 1; j <= tot; j++)
		{
			if (prime[j] * i > len) break;
			mark[prime[j] * i] = 1;
			if (i % prime[j]) phi[i * prime[j]] = (prime[j] - 1) * phi[i];
			else
			{
				phi[i * prime[j]] = prime[j] * phi[i];
				break;
			}
		}
	}
	for (LL i = 1; i <= len; i++)
		ans += phi[i] * (n / (i * i));
	printf("%lld",ans);
	return 0;
}

第四题(d)
【题目描述】
从前有个游戏。
游戏分为k 轮。给定一个由小写英文字母组成的字符串的集合S ,在每轮游戏开
始时,双方会得到一个空的字符串,然后两人轮流在该串的末尾添加字符,并且需要保
证新的字符串是S 中某个串的前缀,直到有一方不能操作,则不能操作的一方输掉这
一轮。新的一轮由上一轮输的人先手,最后一轮赢的人获得游戏胜利。
假定双方都采取最优策略,求第一轮先手的一方能否获胜。
【输入格式】
从文件d.in 中读入数据。
输入包含多组数据。
每组数据的第一行包含两个整数n; k,分别表示字符串的数量和游戏的轮数。
接下来n 行,每行一个由小写英文字母组成的字符串。
【输出格式】
输出到文件d.out 中。
对于每组数据输出一行,若先手能获胜输出HY wins!,否则输出Teacher wins!。
【样例1 输入】
2 3
a
b
3 1
a
b
c
【样例1 输出】
HY wins!
HY wins!
【样例2 输入】
1 2
ab
【样例2 输出】
Teacher wins!
【子任务】
对于40% 的数据,1 n 10; 1 k 104;
对于100% 的数据,1 n 105; 1 k 109,保证所有字符串长度不超过105,数
据组数不超过10。


容易发现

如果先手有必胜策略而没有必败策略,那么这时候最后的胜负只与轮数的奇偶性有关;

如果先手有必胜策略也有必败策略,那么先手可以前面几轮一直输,最后一轮赢,那么这种情况下先手必胜;

如果先手没有必胜策略而有必败策略,那么不管多少轮他都是先手,必败;


然后建一棵trie跑dp就好啦


代码:

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

typedef long long LL;

const int INF = 2147483647;
const int maxlong = 1001000;

char s[maxlong];
int len,tot,ch[maxlong][30],f[maxlong],g[maxlong],rt;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void insert(int &o,int dep)
{
	if (!o) o = ++tot;
	if (dep == len) return;
	insert(ch[o][s[dep + 1] - 'a' + 1],dep + 1);
}

inline void dp(int u)
{
	bool tmp = 1;
	for (int i = 1; i <= 26; i++)
	{
		int v = ch[u][i];
		if (!v) continue;
		tmp = 0;
		dp(v);
		if (!f[v]) f[u] = 1;
		if (!g[v]) g[u] = 1;
	}
	if (tmp) g[u] = 1;
}

int main()
{
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
	int n,k;
	while (scanf("%d",&n) != EOF)
	{
		memset(ch,0,sizeof(ch));
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		k = getint();
		rt = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%s",s + 1);
			len = strlen(s + 1);
			insert(rt,0);
		}
		dp(rt);
		bool ans;
		if (f[rt] && !g[rt]) ans = k & 1;
		if (f[rt] && g[rt]) ans = 1;
		if (!f[rt] && g[rt]) ans = 0;
		printf(ans ? "HY wins!\n" : "Teacher wins!\n");
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值