中南4月16号网络同步赛

题目大意:

1.求方程x^a==b(mod N)解的数量,N是奇数

2.按照方程x^a==b(mod N)的解指示信鸽放飞时间将信鸽分组,每一组不少于k个信鸽,求总延迟最少的分组方法

 

大概做法:

1.N分解因子,分解为p1^e1*p2^e2... ...pi为素数,ei为正整数。

2.对于每一个pi^ei,求出xi^a=bi(mod pi^ei),式中bi=b%pi^ei()。可能有多解,需要算出xi的所有可能解。

3.判断解的总数,最终x^a==b(mod N)解的总数是所有xi^a=bi(mod pi^ei)解总数的乘积)。特判解总数为0的情况,解总数<k的情况,解总数>m的情况。这三种情况直接输出答案。解数特别多时第二步生成解可能超时,因此第二步就要判断解太大的情况。

4.枚举所有xi的取值组合,使用中国剩余定理还原出对应的xx是原方程x^a==b(mod N)的解。

5.对所有解排序,使用动态规划解出最小扣押延迟

 

 

难点:

xi^a=bi(mod pi^ei)的所有解:

bi==0

xi包含p^((ei+a-1)/a)的因子为充要条件,枚举之p^((ei+a-1)/a)的倍数

ei==1时:

pi的一个原根r,求r为底b的离散对数l,则r^l==bi(mod pi),则(r^(l/a))^a=r^l=b(mod pi),xi=r^(l/a),l/a实在mod phi(p)环境下的除法,可能多解或无解

ei!=1时:

bipi互素,做法与e==1相同

bipi不互素,有bi=pi^c *dc不整除a则无解,否则设x=pi^(c/a)*yy^a=d(mod pi^(ei-c)),求出所有y,即可求出x。注意要求出所有x,[0pi^ei-1]范围内的所有x,不要漏掉

 

因子分解:暴力,略。

中国剩余定理:见相关资料,原理略。主要是用中国剩余定理从分解因子后的各个xi^a=bi(mod pi^ei)方程的解还原出原始的x^a==b(mod N)的解,小心中间过程溢出

动态规划:

纯暴力不行。需要斜率优化,或者二分优化。实际上加上决策点的剪枝(i的决策只从>=i-1的决策点里面枚举)就能通过此题。

 

复杂度:

分解因子、离散对手:O(sqrt(n));

斜率优化的动态规划:O(ans),ans为方程解的个数,ans<=m

但动态规划前的排序复杂度为O(ans*log(ans)),是最慢的部分,可以考虑用针对整数的线性算法优化(标程没有优化)

 

Problem A: 十进制-十六进制

Time Limit: 1 Sec   Memory Limit:128 MB
SUBMIT: 241   Solved: 142
 
Description

 把十进制整数转换为十六进制,格式为0x开头,10~15由大写字母A~F表示。

Input

 每行一个整数x,0<= x <= 2^31。

Output

 每行输出对应的八位十六进制整数,包括前导0。

Sample Input

0
1023

Sample Output

0x00000000
0x000003FF

HINT

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int x;
	while(scanf("%d", &x) != EOF)
		printf("0x%08X\n", x);
	return 0;
}


 

Problem B: Sums

Time Limit: 1 Sec  Memory Limit: 16 MBSUBMIT: 294  Solved: 111
Description
Sometimes Ziwen need to operate even larger numbers. A limit of 1000 digits is so small… You have to find the sum of two numbers with maximal size of 1 000 000 digits.
 

Input

The first line contains a single integer N that is the length of the given integers(1 ≤ N ≤ 1 000 000). It is followed by these integers written in columns. That is, the next N lines contain two digits each, divided by a space. Each of the two given integers is not less than 0, and the length of their sum does not exceed N. The integers may contain leading zeroes.

Output

Output exactly N digits in a single line representing the sum of these two integers.

Sample Input

40 44 26 83 7

Sample Output

4750

HINT

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
const int maxn = 1 << 20;
int a[maxn], b[maxn], n;
int main()
{
	int i, cur;
	while(scanf("%d", &n) != EOF)
	{
		for(i = n - 1; i >= 0; -- i)
			scanf("%d%d", &a[i], &b[i]);
		for(i = cur = 0; i < n; ++ i)
		{
			a[i] += b[i] + cur;
			cur = a[i] / 10;
			a[i] %= 10;
		}
		for(i = n - 1; i >= 0; -- i)
			printf("%d", a[i]);
		printf("\n");
	}
	return 0;
}

 

Problem C: Balls in the Boxes

 Time Limit: 1 Sec  Memory Limit: 128 MB
SUBMIT: 431  Solved: 80

Description
Mr. Mindless has many balls and many boxes,he wants to put all the balls into some of the boxes.Now, he wants to know how many different solutions he can have.
you know,he could put all the balls in one box,and there could be no balls in some of the boxes.Now,he tells you the number of balls and the numbers of boxes, can you to tell him the number of different solutions? Because the number is so large, you can just tell him the solutions mod by a given number C.
Both of boxes and balls are all different.
 

Input

 
 
There are multiple testcases. In each test case, there is one line cantains three integers:the number of boxes ,the number of balls,and the given number C separated by a single space.All the numbers in the input are bigger than 0 and less than 2^63.

Output

 For each testcase,output an integer,denotes the number you will tell Mr. Mindless

Sample Input

3 2 44 3 5

Sample Output

14

HINT 

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef long long LL;
LL box, ball, c;
LL MultiMod(LL a, LL b, LL c)
{
	LL res = 0;
	if(!b) return 0;
	while(b > 1)
	{
		if(b & 1) res = (res + a) % c;
		a = (a << 1) % c;
		b >>= 1;
	}
	return (res + a) % c;
}
LL PowMod(LL a, LL b, LL c)
{
	LL res = 1;
	while(b > 1)
	{
		if(b & 1) res = MultiMod(res, a, c);
		a = MultiMod(a, a, c);
		b >>= 1;
	}
	return MultiMod(res, a, c);
}
int main()
{
	while(scanf("%lld%lld%lld", &box, &ball, &c) != EOF)
		printf("%lld\n", PowMod(box, ball, c));
	return 0;
}

 

Problem D: 寒衣调

Time Limit: 1 Sec  Memory Limit:128 MB
SUBMIT: 457  Solved: 86

Description

男从戎,女守家。一夜,狼烟四起,男战死沙场。从此一道黄泉,两地离别。最后,女终于在等待中老去逝去。逝去的最后是换尽一生等到的相逢和团圆。
某日两人至奈何桥前,服下孟婆汤。
每滴孟婆汤都有强度不一的药效,设一碗孟婆汤共N滴(0<N<100000),其中第i滴(0≤i<N)用b[i]表示。
孟婆汤的药效与原料有关,设熬制前同样有N滴原料,第i滴原料用a[i]表示,0≤a[i]<2^32。
药效b[i]的计算方法为b[i]=(a[0]*a[1]*...*a[N-1]/a[i])%m(假设0/0=1),0<b[i]<2^32。

Input

 
 
每行开头给出原料数量N,取模数m,紧接着的一行按顺序给出原料a[i]。求出熬制所成孟婆汤的药效b[i],每次输完一碗孟婆汤的药效后以换行结尾。

Output

 求出熬制所成孟婆汤的药效b[i],每碗孟婆汤后以换行结尾。

Sample Input

5 11
2 7 5 3 9
3 7
9 8 5

Sample Output

10 6 4 3 1
5 3 2

HINT

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
const int maxn = 1 << 17;
typedef long long LL;
int a[maxn], b[maxn], n, m;
void ReadData()
{
	int i;
	for(i = 0; i < n; ++ i)
		scanf("%d", &a[i]);
}
void Pre()
{
	int i;
	for(i = n - 1, b[n] = 1; i >= 0; -- i)
		b[i] = (LL)b[i + 1] * a[i] % m;
	for(i = 1; i < n; ++ i)
		a[i] = (LL)a[i - 1] * a[i] % m;
}
void Prin()
{
	int i;
	printf("%d", b[1] % m);
	for(i = 1; i < n; ++ i)
		printf(" %d", (LL)a[i - 1] * b[i + 1] % m);
	printf("\n");
}
int main()
{
	while(scanf("%d%d", &n, &m) != EOF)
	{
		ReadData();
		Pre();
		Prin();
	}
	return 0;
}

 

Problem E: Dominating

Time Limit: 1 Sec  Memory Limit: 128 MB
SUBMIT: 157  Solved: 45

Description

 
 
Dota是一款在大学中很盛行的游戏,10个人一起玩,分为两个团队:近卫和天灾,每个团队有5名玩家,游戏中以推塔为目的,但是更多的人却以杀人为乐趣,每杀一个人都有一定的经济作为奖励,当然如果被杀就会扣除一些经济作为惩罚了。为了简化规则,我们规定如下:
每个人的First为他杀第一个人的时候或者是他死后杀的第一个人的时候,Second表示他在杀第一个人和第二个人中间没有被敌方单位或队友杀过,Spree表示Second和杀第三个人之间没有被敌方单位或队友杀过,Dominating表示Spree和杀第4个人之间没有被敌方单位或队友杀过,Mega表示Dominating和杀第5个人之间没有被敌方单位或队友杀过,Unstop表示Mega和杀第6个人之间没有被敌方单位或队友杀过,Wicked表示Unstop和杀第7个人之间没有被敌方单位或队友杀过,Monster表示Wicked和杀第8个人之间没有被敌方单位或队友杀过,God表示Monster和杀第9个人之间没有被敌方单位或队友杀过,Holy表示God和杀第10人或者超过第10个人之间没有被敌方单位或队友杀过。
BFirst则表示该玩家第一次被敌方单位或队友杀死或者杀人之后第一次被敌方单位或队友杀死,BSecond表示BFirst和第二次被敌方单位或队友杀死之间没有杀过人,BSpree表示BSecond和第3次被敌方单位或队友杀死之间没有杀过人,依次类推
先给出杀人之后所得的金钱和死亡之后会扣除的金钱

First

200

BFirst

-200

Second

275

BSeconde

-275

Spree

325

BSpree

-325

Dominating

400

BDominating

-400

Mega

475

BMega

-475

Unstop

575

BUnstop

-575

Wicked

675

BWicked

-675

Monster

800

BMonser

-800

God

900

BGod

-900

Holy

1000

BHoly

-1000

现在给出一份各个玩家之间的杀与被杀的关系,请你求最后每位玩家剩下多少钱,这里不考虑系统发钱,每个玩家最开始的金钱为4726,如果某次死的时候,玩家身上没有足够的金钱,那么他的金钱会被扣到0,但是杀死他的玩家得到的金钱不变,自杀不会有金钱损失,同时也没有人会得到金钱,该玩家的杀敌状态也不变,被队友杀死的时候,死的玩家会掉钱,杀敌数变为0,杀人的玩家,杀敌数不变,同时没有玩家会得到金钱

Input

 多组测试数据,每组数据有多行,开始10行表示每个玩家的名字,前5个为近卫的玩家,后5个为天灾的玩家,接下来有一行,一个数字N(N<=100)表示接下来有多少行杀和被杀的数据,接下来有N行,每行有两个玩家的名字(用空格隔开),表示前面那个玩家杀死后面那个玩家。所有的玩家姓名为不超过15个字符的字符串

Output

 对于每组测试数据,按照输入的顺序输出每个玩家最后的金钱数目,每个玩家一行,表示他的金钱

Sample Input

PN
klion26
wdy0504
EX
Kelp
pkudream
wxt
Kaer
CC
STHM
7
PN  pkudream
EX  wxt
wdy0504	Kaer
CC Kelp
klion26 STHM
wxt	PN
wxt EX

Sample Output

4726
4926
4926
4726
4526
4526
5001
4526
4926
4526

HINT

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int K[11] = {0, 200, 275, 325, 400, 475, 575, 675, 800, 900, 1000};
int trie[200][128], tp, bh;
int situ[11], money[11];
char name[10][20], w[20], l[20];
int MakeT(char *x)
{
	int i, t, cur = 0;
	for(i = 0; x[i]; ++ i)
	{
		if(trie[cur][x[i]] == -1)
			trie[cur][x[i]] = ++ tp;
		cur = trie[cur][x[i]];
	}
	if(trie[cur][0] == -1) trie[cur][0] = bh ++;
	return trie[cur][0];
}
void win(char *x)
{
	int ith = MakeT(x);
	if(situ[ith] < 0)
		situ[ith] = 0;
	++ situ[ith];
	if(situ[ith] > 10) situ[ith] = 10;
	money[ith] += K[situ[ith]];
}
void lose(char *x)
{
	int ith = MakeT(x);
	if(situ[ith] > 0)
		situ[ith] = 0;
	-- situ[ith];
	if(situ[ith] < -10) situ[ith] = -10;
	money[ith] -= K[-situ[ith]];
	if(money[ith] < 0) money[ith] = 0;
}
int main()
{
	int i, j, k, n;
	while(scanf("%s", name[0]) != EOF)
	{
		tp = bh = 0;
		memset(trie, -1, sizeof(trie));
		situ[MakeT(name[0])] = 0, money[0] = 4726;
		for(i = 1; i < 10; ++ i)
		{
			scanf("%s", name[i]);
			situ[MakeT(name[i])] = 0;
			money[i] = 4726;
		}
		scanf("%d", &n);
		while(n --)
		{
			scanf("%s%s", w, l);
			if(MakeT(w) == MakeT(l)) continue;
			if(MakeT(w) / 5 != MakeT(l) / 5)
				win(w);
			lose(l);
		}
		for(i = 0; i < 10; ++ i)
			printf("%d\n", money[i]);
	}
	return 0;
}

 

Problem F: Nearest Numbers

 Time Limit: 1 Sec  Memory Limit:32 MB
SUBMIT: 248  Solved: 65

Description

 Given three integer sequences which have been sorted in ascending order,pick one integer from each sequence and we can get three integers.we want these three integers to be nearest to each other.Assuming these three integers as a,b,c, they are nearest to each other means d=(a-b)2+(b-c)2+(c-a)2 is the smallest.

Input

 There are many test cases. For each test case,the first line are three integers la,lb,lc, they let you know the length of each sequence. The following three lines separately have la,lb,lc integers,give you the integers in each sequence. 1<=la,lb,lc<=10^6 and All integers in the sequences are in the range of [-10^9,10^9].

Output

For each test case, just print one integer : the smallest d as mentioned above.

Sample Input

10 10 10
1 2 3 4 5 6 7 8 9 10
2 3 6 8 10 12 14 16 18 20
3 5 7 9 11 12 14 16 18 20

Sample Output

0

HINT

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
const int maxn = 1 << 20;
typedef long long LL;
LL a[maxn], b[maxn], c[maxn];
inline LL min(LL x, LL y)
{return x < y ? x : y;}
inline LL cal(LL x, LL y, LL z)
{return (x - y) * (x - y) + (x - z) * (x - z) + (z - y) * (z - y);}
int main()
{
	LL ans, tmpa, tmpb, tmpc;
	int n1, n2, n3, i, j, k;
	while(scanf("%d%d%d", &n1, &n2, &n3) != EOF)
	{
		for(i = 0; i < n1; ++ i) scanf("%lld", &a[i]);
		for(i = 0; i < n2; ++ i) scanf("%lld", &b[i]);
		for(i = 0; i < n3; ++ i) scanf("%lld", &c[i]);
		ans = ((LL)1 << 63) - 1;
		i = j = k = 0;
		while(i < n1 && j < n2 && k < n3 && ans > 0)
		{
			ans = min(ans, cal(a[i], b[j], c[k]));
			tmpa = cal(a[i + 1], b[j], c[k]);
			tmpb = cal(a[i], b[j + 1], c[k]);
			tmpc = cal(a[i], b[j], c[k + 1]);
			if(tmpa < tmpb && tmpa < tmpc) ++ i;
			else if(tmpb < tmpc) ++ j;
			else ++ k;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

 

Problem G: 二阶魔方

 Time Limit: 2 Sec  Memory Limit: 128 MB
SUBMIT: 21  Solved: 6

Description

 最近Grandeur买了两个魔方,拆开包裹的时候,室友都围了过来。“还有这种魔方,这多简单……”。因为有一个二阶魔方,别看它小,也不是好欺负的。Grandeur把魔方转动了几下递给他,说道“就不用完全复原了,你把上下相对的黄色和白色两个面弄出来试试咯。”于是他掰了一下午……

原来二阶魔方也不是那么简单的。Grandeur现在想知道对于一个特定的情况,最少需要几步可以达到黄白两面复原的状态呢(任意面任意方向旋转90度为一步)?

Input

 
 
 如图所示,按照二阶魔方的平面展开图给每一个方格编号。
第一行一个整数T,表示有T组数据。接下来T行每行24个空格隔开的数,表示0~23号格子的颜色,分别为0:黄色,1:白色,2:蓝色,3:绿色,4:红色,5:橙色。
输入数据保证是合法的状态,魔方没有拆开过。

Output

每组数据输出一个整数,表示达到目标状态需要的最少步数。

Sample Input

24 4 1 2 0 4 3 4 2 0 2 0 0 3 5 5 5 1 5 2 3 3 1 14 4 4 4 0 0 3 3 2 2 0 0 5 5 5 5 1 1 2 2 3 3 1 1

Sample Output

21

HINT

 编号只是方便描述数据,复原的魔方不一定要颜色和图中编号一一对应的。 

 

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
using namespace std;
typedef long long LL;
const int HashMax = 1000003;
const int inf = 0x3f3f3f3f;
int ans[HashMax];
LL Hash[HashMax];
int ye, wh;
int RO[6][24]=	{
				{0,9,2,11,4,5,6,7,8,13,10,15,12,22,14,20,18,16,19,17,3,21,1,23},
				{0,22,2,20,4,5,6,7,8,1,10,3,12,9,14,11,17,19,16,18,15,21,13,23},
				{0,1,7,5,4,12,6,13,10,8,11,9,18,16,14,15,2,17,3,19,20,21,22,23},
				{0,1,16,18,4,3,6,2,9,11,8,10,5,7,14,15,13,17,12,19,20,21,22,23},
				{0,1,2,3,4,5,22,23,8,9,6,7,14,12,15,13,16,17,10,11,20,21,18,19},
				{0,1,2,3,4,5,10,11,8,9,18,19,13,15,12,14,16,17,22,23,20,21,6,7}
				};
int MakeHash(int y, int w)
{
	LL situ = (LL)y << 24 | w, big = situ % HashMax;
	while(Hash[big] != -1 && Hash[big] != situ)
		big = (big << 1 | 1) % HashMax;
	if(Hash[big] == -1) Hash[big] = situ;
	return big;
}

inline int min(int a, int b)
{return a < b ? a : b;}

struct S
{int y, w, pace;};
queue<S> q;
void bfs()
{
	S lin, nex;
	int i, j, situ;
	lin.pace = 0;
	lin.y = 0xf0000, lin.w = 0xf0, q.push(lin);
	ans[MakeHash(lin.y, lin.w)] = 0;
	lin.y = 0xf0, lin.w = 0xf0000, q.push(lin);
	ans[MakeHash(lin.y, lin.w)] = 0;
	lin.y = 0xf00000, lin.w = 0xf00, q.push(lin);
	ans[MakeHash(lin.y, lin.w)] = 0;
	lin.y = 0xf00, lin.w = 0xf00000, q.push(lin);
	ans[MakeHash(lin.y, lin.w)] = 0;
	lin.y = 0xf000, lin.w = 0xf, q.push(lin);
	ans[MakeHash(lin.y, lin.w)] = 0;
	lin.y = 0xf, lin.w = 0xf000, q.push(lin);
	ans[MakeHash(lin.y, lin.w)] = 0;
	while(!q.empty())
	{
		lin = q.front(), q.pop();
		for(i = 0; i < 6; ++ i)
		{
			nex.y = nex.w = 0;
			for(j = 0; j < 24; ++ j)
			{
				nex.y |= (lin.y >> RO[i][j] & 1) << j;
				nex.w |= (lin.w >> RO[i][j] & 1) << j;
			}
			nex.pace = lin.pace + 1;
			situ = MakeHash(nex.y, nex.w);
			if(ans[situ] == -1)
			{
				ans[situ] = nex.pace;
				q.push(nex);
			}
		}
	}
}
int main()
{
	int i, j, T;
	memset(Hash, -1, sizeof(Hash));
	memset(ans, -1, sizeof(ans));
	bfs();
	for(scanf("%d", &T); T --; )
	{
		for(i = ye = wh = 0; i < 24; ++ i)
		{
			scanf("%d", &j);
			if(j == 0) ye |= 1 << i;
			if(j == 1) wh |= 1 << i;
		}
		printf("%d\n", ans[MakeHash(ye, wh)]);
	}
	return 0;
}

 

Problem H: Plumber

 Time Limit: 1 Sec  Memory Limit: 64 MB
SUBMIT: 79  Solved: 29

Description

       Mr.Croft is a popular plumber(水管工) in this city,He is very good at analysis the structure of the pipe, so as to find out the problem. However, along with the progress of the water conservancy construction(水利建设) in this city, the structure of the water pipe is also more and more complex, which bring him a great challenge. 
       One day, he was sent to a community to fix the water pipes of some families. He first found the water pipe structure maps (as shown below)of the community,and Then he found this area have one water inlet and several water outlets, in total N openings, he marked the water inlet as 0, and the water outlets from 1 to N-1; In the structure map,there are M connecting points of the water pipes, marked from N to N + M-1; In the map,the pipes can be seen as small sections divided by connecting points and the water inlets or the water outlets. There are L sections without valves(阀门) and K sections with valves.

              

       His mission is just need to shut down several valves, making somebody's water outlets no longer have the water out, so that he can repair the family's water pipes. There are a total of T families' water pipes need to repair, but he needs to repair them one by one. In order to improve the efficiency, he wants to know how many valves at least he needs to turn off to cut the target family's water supply.

Input

       There are many test cases. For each test case,the first line gives 5 integers :M,N,L,K,T as mentioned above.Then there are L lines,and each line gives 2 integers X,Y representing the two endpoints of the pipe sections.Then there are K lines,and each line gives 2 integers X,Y representing the two endpoints of the pipe sections with a valve(the first line represents valve with mark 1,the second represents valve with mark 2....and so on).At last there are T lines,with each line there is only one integer Z, representing the mark of the water outlet need to repair.
       (2<=N<=1000,2<=M+N<=2000,0<=K<=5000, 1<=T<=min(10,N),0<=X,Y<=M+N-1,1<=Z<=N-1)

Output

       For each test case,just print the least number of the valves that need to be shut down.If there's no solution to let the target outlet be without water,then just print "No solution!" instead.After each test case,print a blank line.

Sample Input

9 5 11 6 2
0 6
1 5
6 11
11 10
5 8
8 3
7 9
7 2
12 13
9 12
4 13
5 6
7 10
8 9
12 11
10 13
1 2
1 
2
17 7 19 11 2
0 9
9 13
13 23
1 7
2 10
3 14
4 15
14 15
15 16
16 22
6 21
8 21
17 18
18 19
7 19
12 22
11 12
20 22
23 10
7 8
10 11
14 17
16 18
5 20
21 23
8 9
12 13
11 17
20 19
21 13 
3
2

Sample Output

3
4

3
No solution!

HINT

 
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
using namespace std;
const int maxn = 1 << 11;
const int maxm = 1 << 14;
int p[maxn], fst[maxn], lay[maxn], work[maxn], M, N, L, K, T, wnum, sou, ter;
int flow[maxm], cap[maxm], u[maxm], v[maxm], nex[maxm];
void init()
{
	int i;
	for(i = M + N; i >= 0; -- i) p[i] = i;
	memset(fst, -1, sizeof(fst));
	wnum = 0;
}

int fa(int i)
{return p[i] == i ? i : p[i] = fa(p[i]);}

void AddEdge(int s, int e)
{
	u[wnum] = s;
	v[wnum] = e;
	cap[wnum] = 1;
	nex[wnum] = fst[s];
	fst[s] = wnum;
	++ wnum;
}
void DbEdge(int s, int e)
{
	AddEdge(s, e);
	AddEdge(e, s);
}
void ReadGraph()
{
	int s, e;
	while(L --)
	{
		scanf("%d%d", &s, &e);
		p[fa(s)] = fa(e);
	}
	while(K --)
	{
		scanf("%d%d", &s, &e);
		DbEdge(fa(s), fa(e));
	}
}


/********************网络流 ********************/
inline int min(int a, int b)
{return a < b ? a : b;}
queue<int> q;
const int inf = 0x3f3f3f3f;
int bfs()
{
	int i, j;
	while(!q.empty()) q.pop();
	memset(lay, -1, sizeof(lay));
	q.push(sou), lay[sou] = 0;
	while(!q.empty())
	{
		for(i = fst[q.front()], q.pop(); i != -1; i = nex[i])
			if(cap[i] > flow[i] && lay[v[i]] == -1)
			{
				lay[v[i]] = lay[u[i]] + 1;
				if(v[i] == ter)
					return 1;
				q.push(v[i]);
			}
	}
	return 0;
}
int dfs(int cur, int inc)
{
    if(cur == ter)
        return inc;
    for(int &i = work[cur]; i != -1; i = nex[i])
        if(flow[i] < cap[i] && lay[v[i]] == lay[cur] + 1)
            if(int t = dfs(v[i], inc < cap[i] ? inc : cap[i]))
            {
                flow[i] += t;
                flow[i ^ 1] -= t;
                return t;
            }
    return 0;
}
int dinic()
{
    int res = 0, t;
    memset(flow, 0, sizeof(flow));
    while(bfs())
    {
        memcpy(work, fst, sizeof(fst));
		while(t = dfs(sou, inf))
            res += t;
    }
    return res;
}
/********************网络流 ********************/

void MakeAns()
{
	sou = fa(0);
	while(T --)
	{
		scanf("%d", &ter);
		ter = fa(ter);
		if(sou == ter)
			printf("No solution!\n");
		else
			printf("%d\n", dinic());
	}
}
			
int main()
{
	while(scanf("%d%d%d%d%d", &M, &N, &L, &K, &T) != EOF)
	{
		init();
		ReadGraph();
		MakeAns();
		printf("\n");
	}
	return 0;
}

 
 
 

Problem I: Big party

Time Limit: 5 Sec  Memory Limit: 256 MB SUBMIT: 20  Solved: 5

Description

 鸡姨和刚妈 正准备下个周末的Party。他们想邀请很多的朋友,其中有很多男生,也有很多女生,大家一起交流感情。她们家中有很多桌子,其中有一个很大圆桌,大家都要到这个圆桌上来玩。但是 鸡姨和刚妈 现在不知道如何分配座次,这是为什么呢?据说当有超过K个女孩座位相邻(即这些女孩的座位是连续的,中间没有男孩)的话,她们就会说一整晚的话而不和其他人聊天,男生们会因此很寂寞,也达不到Party的目的。鸡姨和刚妈 没有其他选择,只有求助她们的好朋友Ziwen。

问题简化成如下:
N个人围绕着圆桌坐着,其中一些是男孩,另一些是女孩。你的任务是找出所有合法的方案数,使得不超过K个女孩座位是连续的。
当然,需要注意的是这个桌子是个圆的,循环同构会被认为是同一种方案。

Input

 
 
第一行有一个数T,表示以下有T组数据,每组数据有两个整数N,K(N,K<=2000)。每组数据之间有一个空行隔开。

Output

 
 
输出T行,每行顺次对应一组测试数据。
每组数据你需要输出最后的方案数除以100000007的余数。

Sample Input

33 13 34 1

Sample Output

243

HINT

 
第一组数据的方案是:MMM,MMW (M是男孩, W是女孩)。
 
第二组数据的方案是:MMM,MMW,MWW,WWW。
 
第三组数据的方案是:MMMM, MMMW,MWMW。
 
#include<iostream>
#include<cstdio>
using namespace std;

const int MaxN=2000;
const long long ModNum=100000007;

long long P[MaxN+1][MaxN+1],R[MaxN+1],Pow[MaxN+1],Ans;
int Loo,Loop,N,K;

long long ModExp(long long Base,long long T)
{
	if(T==0)
		return 1;
	long long Res=ModExp(Base,T>>1);
	Res=(Res*Res)%ModNum;
	if((T&1)==1)
		Res=(Res*Base)%ModNum;
	return Res;
}

void First()
{
	for(int i=1;i<=MaxN;i++)
		Pow[i]=ModExp(i,ModNum-2);
}

void Init()
{
	scanf("%d%d",&N,&K);
	K=min(K,N);
}

void Work()
{
	int i,j;
	P[1][0]=1;
	for(i=1;i<N;i++)
	{
		P[i+1][0]=0;
		for(j=0;j<=K;j++)
		{
			P[i+1][0]=(P[i+1][0]+P[i][j])%ModNum;
			P[i+1][j+1]=P[i][j];
		}
	}
	for(i=1;i<=N;i++)
		for(j=0;j<=K;j++)
			P[i][j]=(P[i][j]*(j+1))%ModNum;
	for(i=1;i<=N;i++)
	{
		R[i]=0;
		for(j=0;j<=K;j++)
			R[i]=(R[i]+P[i][j])%ModNum;
	}
	if(K>=N)
		Ans=1;
	else
		Ans=0;
	for(i=1;i<=N;i++)
		if((N%i)==0)
		{
			for(j=2*i;j<=N;j+=i)
				R[j]=(R[j]-R[i]+ModNum)%ModNum;
			Ans=(Ans+R[i]*Pow[i])%ModNum;
		}
}

void Print()
{
	printf("%lld\n",Ans);
}

int main()
{
	First();
	scanf("%d",&Loop);
	for(Loo=1;Loo<=Loop;Loo++)
	{
		Init();
		Work();
		Print();
	}
	return 0;
}

 
 
 
 

Problem J: 飞鸽传书

Time Limit: 1 Sec   Memory Limit: 128 MB SUBMIT: 37   Solved: 12

Description

     很久很久以前,有一位将军被派到前线指挥战斗。前线离都城很遥远,将军希望使用信鸽向都城汇报战况。

    但当时的信鸽不能单独飞行如此遥远的距离,因此将军在后方的营地安排了一位联络员,营地离前线很近,离都城很远。信鸽可以单独从前线飞到营地,但不能单独从营地飞到都城。
    为了解决这个问题,营地的联络员收到从前线飞来的信鸽后,不会让信鸽单独飞到都城,而是让它留在营地,等营地有了一定数量的信鸽后,将这一批信鸽组成一个队伍,一起飞往遥远的都城。团结才是力量,为了让信鸽在飞往都城的遥远的路途中不至于迷失,每一批至少要有k只信鸽一起飞行。
    整个战斗预计要持续N分钟,N是奇数。将军已和联络员约定,从战斗开始起计时,对于第t分钟(t表示从战斗开始起计时的分钟数,取值为[0,N-1], 战斗开始时t=0,第N-1分钟过后战斗结束),如果(t^a)%N==b(t^a表示t的a次方),则将军在第t分钟立即从前线放出一只信鸽飞往营地汇报最新的战斗进展。将军带着m只供放飞的信鸽到了前线,前线离营地非常近,可以认为从将军放出的信鸽会立刻到达营地。但由于营地必须让信鸽分批飞到都城,信鸽可能会被暂时留在营地,因此会延误一些战况的汇报。
    当然,联络员希望,这个延误的时间越少越好。如果一只信鸽在t1时刻从前线放飞并到达营地,但是t2时刻才从营地起飞飞往都城,则因扣留在营地引起的延时是t2-t1。联络员是很聪明的,他希望设计一个分批放飞的策略,让所有信鸽的总延时尽可能少。
例如,如果战斗预计持续时间N=13 分钟,将军在和联络员约定了a=4,b=3。则将军按照在(t^a)%N==b(t的a次方除N的余数等于b)时刻t放飞信鸽的策略,将军在第2、3、10、11这4个时刻放出一共4只信鸽,这些信鸽一放出就立即到达营地。如果信鸽至少要k=2个为一批一起才能远程飞行,联络员可以选择在第3分钟让第1、2只信鸽作为一组飞往都城,第11分钟让第3、4只信鸽作为一组飞往都城,这样总延迟是2分钟(第1、3只信鸽各延迟1分钟)。
    联络员想知道的问题是:
    1.按照将军在(t^a)%N==b时刻放飞信鸽的计划,在整个N分钟的战斗中应该有多少只信鸽被发送?
    2.如果联络员按照最好的分组方法把这些信鸽分批飞往都城,那么所有的信鸽被扣留在营地造成的总延迟最少是多少?
    当然可能会有以一些情况导致将军的汇报计划不能完成,比如它总共放出的信鸽数量还不足编为一组(要发出的总信鸽数<k),又或者将军带去的供发送的信鸽数不够(要发出的总信鸽数>m)。

Input

     第一行一个整数T(1<=T<=50),表示数据的组数。

    接下来T组测试数据每个一行,包括5整数:
    N (3<=N<1000,000,000,且N为奇数),表示战斗将持续的总分钟数
    k (1<=k<=N),表示一组信鸽至少要k只才能长途飞行
    a (0<=a<N),将军与联络员约定的发送时刻t^a%N==b中的a
    b (0<=b<N),将军与联络员约定的发送时刻t^a%N==b中的b
   m (1<=m<=100,000),将军带去的供发送战况的信鸽总数。如果按照计划要发送的所有信鸽数多于这个值,则不能完成原有的汇报计划

Output

     每组测试数据输出一行,每行两个整数,第一个整数表示按照将军计划,将要发出的信鸽数量;第二个数表示信鸽被扣留在营地造成的最少总延迟(分钟数)。如果将军带去的信鸽数不够,第二个数输出-1;如果将军带去的信鸽足够,但营地无法将这些信鸽分组发回都城,第二个数输出-2。如果按照将军的计划,根本不需要发送信鸽,仍然算是可以成功发送的,扣留的总延迟为0(两个数都输出0)。

Sample Input

5
13 2 4 3 10
125 2 2 25 100
225 10 60 1 1000
1517 100 7 103 10000
1000003 10 1000002 1 1000000

Sample Output

4 2
10 75
120 1020
1 -2
1000002 -1

HINT

 

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef long long U64;
const int MAXF = 16;
const int MAX_ANS = 100005;
int fact_val[MAXF];
int fact_cnt[MAXF];
int fact_pow[MAXF];
int ans_cnt[MAXF];
int ans_array[MAXF][MAX_ANS];
int g_fact_num, g_tot_cnt, g_n, g_k, g_m, g_a, g_b;
int china_x[MAXF], china_Mi[MAXF];
int S[MAX_ANS], A[MAX_ANS], g_final_p;
U64 sum[MAX_ANS], dp[MAX_ANS];
const int HZ = 43963;
const int MXB = 200000;
struct node
{
	int key;
	int val;
	node *next;
};
node BUF[MXB], *BP;
node *hash[HZ];
int pow_mod(int a, int b, int n)
{
	int res = 1;
	while (b)
	{
		if (b & 1)
			res = (U64) res * a % n;
		a = (U64) a * a % n;
		b >>= 1;
	}
	return res;
}
int ext_gcd(int a, int b, int &x, int &y)
{
	int t, res;
	if (b == 0)
	{
		x = 1, y = 0;
		return a;
	}
	res = ext_gcd(b, a % b, x, y);
	t = x, x = y, y = t - a / b*y;
	return res;
}
int primitiv_root(int p)
{
	int res, i, arr[20], fn = 0, phi = p - 1;
	for (i = 2; i * i <= phi; i++)
	{
		if (phi % i == 0)
		{
			arr[fn++] = i;
			while (phi % i == 0)
				phi /= i;
		}
	}
	if (phi > 1)arr[fn++] = phi;
	for (res = 2; res < p; res++)
	{
		for (i = 0; i < fn; i++)
			if (pow_mod(res, (p - 1) / arr[i], p) == 1)
				break;
		if (i >= fn)
		{
			if (res % 2 == 0)
				res += p;
			if (pow_mod(res, p - 1, p * p) == 1)
				res += 2 * p;
			break;
		}
	}
	return res;
}
inline void insert(int key, int val)
{
	int k = key % HZ;
	BP->key = key;
	BP->val = val;
	BP->next = hash[k];
	hash[k] = BP++;
}
inline int find(int key)
{
	for (node *p = hash[key % HZ]; p != NULL; p = p->next)
	{
		if (p->key == key)
			return p->val;
	}
	return -1;
}
int log_mod(int a, int b, int n, int phi_n)
{
	int i, e, T, iv, v, x, y;
	if (b == 1)return 0;
	T = (int) (sqrt((double) phi_n) + 1.0);
	iv = pow_mod(a, T, n);
	ext_gcd(iv, n, x, y);
	v = (x + n) % n;
	memset(hash, 0, sizeof (hash));
	BP = BUF;
	for (e = 1, i = 0; i < T; i++)
	{
		if (find(e) != -1)
			break;
		insert(e, i);
		e = ((long long) e * a) % n;
	}
	for (i = 0; i < T; i++)
	{
		if ((e = find(b)) != -1)
			return i * T + e;
		b = ((long long) b * v) % n;
	}
	return -1;
}
int calc_all_atom()
{
	int idx;
	g_tot_cnt = 1;
	for (idx = 0; idx < g_fact_num; idx++)
	{
		int a = g_a, b = g_b % fact_pow[idx], p = fact_val[idx];
		if (b == 0)
		{
			int temp_a = pow_mod(p, (fact_cnt[idx] + a - 1) / a, g_n);
			ans_cnt[idx] = fact_pow[idx] / temp_a;
			g_tot_cnt *= ans_cnt[idx];
			if (g_tot_cnt > g_m)
			{
				continue;
			}
			for (int i = 0; i * temp_a < fact_pow[idx]; i++)
			{
				ans_array[idx][i] = i*temp_a;
			}
		} else
		{
			int g = __gcd(b, fact_pow[idx]);
			int log_g = 0;
			while (pow_mod(p, log_g, g_n) != g)log_g++;
			if (log_g % a != 0)
			{
				return (g_tot_cnt = 0);
			}
			int ans_ext_fact = pow_mod(p, log_g / a, g_n);
			int temp_b = b / g;
			int temp_mod_base = fact_pow[idx] / g;
			int phi_base = temp_mod_base - temp_mod_base / p;
			int temp_a, x, y;
			temp_a = ext_gcd(a, phi_base, x, y);
			if (pow_mod(temp_b, (phi_base) / temp_a, temp_mod_base) != 1)
			{
				return (g_tot_cnt = 0);
			}
			ans_cnt[idx] = temp_a * (g / ans_ext_fact);
			g_tot_cnt *= ans_cnt[idx];
			if (g_tot_cnt > g_m)continue;
			temp_b = pow_mod(temp_b, x + phi_base, temp_mod_base);
			int ans, step;
			if (temp_a == 1)
			{
				ans = temp_b;
				step = 1;
			} else
			{
				int r = primitiv_root(p) % temp_mod_base;
				int log_b = log_mod(r, temp_b, temp_mod_base, phi_base);
				ans = pow_mod(r, log_b / temp_a, temp_mod_base);
				step = pow_mod(r, phi_base / temp_a, temp_mod_base);
			}
			for (int i = 0; i * ans_ext_fact < g; i++)
			{
				for (int j = 0; j < temp_a; j++)
				{
					ans_array[idx][i * temp_a + j] = (ans + i * temp_mod_base) * ans_ext_fact;
					ans = (U64) ans * step % temp_mod_base;
				}
			}
		}
	}
	return g_tot_cnt;
}
void ana(int n)
{
	g_fact_num = 0;
	for (int i = 3; i * i <= n; i += 2)
	{
		if (n % i == 0)
		{
			fact_val[g_fact_num] = i;
			fact_cnt[g_fact_num] = 0;
			fact_pow[g_fact_num] = 1;
			do
			{
				n /= i;
				fact_cnt[g_fact_num]++;
				fact_pow[g_fact_num] *= i;
			}
			while (n % i == 0);
			g_fact_num++;
		}
	}
	if (n > 1)
	{
		fact_val[g_fact_num] = n;
		fact_pow[g_fact_num] = n;
		fact_cnt[g_fact_num++] = 1;
	}
}
void china_dfs(int deep, int res)
{
	for (int i = 0; i < ans_cnt[deep]; i++)
	{
		U64 x = china_x[deep];
		int Mi = china_Mi[deep];
		int mi = fact_pow[deep];
		int nres = (res + (x * (U64) ans_array[deep][i] % mi) * Mi) % g_n;
		if (deep == g_fact_num - 1)
		{
			A[g_final_p++] = nres;
		} else
		{
			china_dfs(deep + 1, nres);
		}
	}
}
void calc_final_ans()
{

	int i, x, y;
	for (i = 0; i < g_fact_num; i++)
	{
		china_Mi[i] = g_n / fact_pow[i];
		ext_gcd(china_Mi[i], fact_pow[i], x, y);
		china_x[i] = (x + fact_pow[i]) % fact_pow[i];
	}
	g_final_p = 0;
	china_dfs(0, 0);
}
inline double slope(int x, int y)
{
	return ((double) dp[y] + (double) sum[y] - (double) dp[x] - (double) sum[x]) / (y - x);
}
U64 dp_solve(int n, int k)
{
	int i, t, L = 0, R = -1;
	sort(A, A + n);
	sum[0] = A[0];
	for (i = 1; i < n; i++)
	{
		sum[i] = A[i] + sum[i - 1];
	}
	for (i = k - 1; i < n && i < k + k - 1; i++)
		dp[i] = A[i]*(U64) (i + 1) - sum[i];
	for (; i < n; i++)
	{
		while (L < R && slope(S[R - 1], S[R]) >= slope(S[R], i - k))R--;
		S[++R] = i - k;
		while (L < R && slope(S[L], S[L + 1]) < A[i]) L++;
		t = S[L];
		dp[i] = dp[t] + A[i]*(U64) (i - t)-(sum[i] - sum[t]);
	}
	return dp[n - 1];
}
int main()
{
	int t;
	U64 res;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d%d%d%d", &g_n, &g_k, &g_a, &g_b, &g_m);
		ana(g_n);
		calc_all_atom();
		if (g_tot_cnt == 0)
		{
			res = 0;
		} else if (g_tot_cnt > g_m)
		{
			res = -1;
		} else if (g_tot_cnt < g_k)
		{
			res = -2;
		} else
		{
			calc_final_ans();
			res = dp_solve(g_tot_cnt, g_k);
		}
		printf("%d %lld\n", g_tot_cnt, res);
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值