2019.11.24 第一次摸底补题报告
题目描述:
有n个容量不一的瓶子,现在想往瓶子里装水,但是一个一个装太累了。于是为了不那么累,就只好佛系装水了。规则就是每次随机选中一个瓶子,往该瓶子里装水,若该瓶子里水满了,就找到下一个瓶子,直至找到最后一个瓶子。现在想知道,经过m次随机操作后,每个瓶子里有多少水。
输入:
一个整数t(1≤t≤10),代表t组数据
每组数据输入一个n和m,代表瓶子数和操作数(1≤n,m≤105)
接下来一行n个整数,代表每个瓶子的容量(≤109)
接下来m行每行2个数x和y代表往第x个瓶子里装y升的水(1≤x≤n,1≤y≤109)
输出:
对于每组输出n个数。
样例输入:
1
7 4
3 4 5 5 6 7 8
3 3
1 8
5 6
6 9
样例输出:
3 4 4 0 6 7 2
自己的题解:先建一个数组给代表瓶子的容量,再给瓶子装水,水满了就继续装下一个瓶子,更新瓶子的容量就行。
以下是代码:
#include
#include
using namespace std;
long long int b[100005]; //一开始没用long long 导致报错。
long long int a[100005];
int main () {
int t;
cin >> t;
while (t--) {
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
int n, m, x, y;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> x >> y;
if (b[x] == 0) {
b[x] = y;
} else {
b[x] = b[x] + y;
}
}
for (int i = 1; i <= n; i++) {
if(b[i] > a[i]) {
b[i + 1] = b[i + 1] + b[i] - a[i];
b[i] = a[i];
}
}
for (int i = 1; i <= n; i++) {
if(i == n) {
cout << b[i];
} else {
cout << b[i] << " ";
}
}
cout << endl;
}
return 0;
}
题目描述:
给定一个整数n,能不能将其分解为三个素数之和,有多少种分法。
例如7=2+2+3,注:(2,2,3),(2,3,2)为同一种
输入:
第一行一个整数t,(1≤t≤10)
接下来t行,每行一个整数n,(1≤n≤4∗104)
输出:
对于每一整数n输出一个数字
样例输入:
3
4
5
9
样例输出:
1
1
2
自己的题解:用Prime数组存储1~40000里面的素数,再从里面进行选择,因为已经拍好循了,就只用保证循环从前面的一个数开始。
(一开始用了3个for循环导致报错,经提醒第三个数可表示为 n-prime[i]-prime[j]。多谢大佬们)
以下是代码:
#include
using namespace std;
int Prime[6000];
int C[400005];
bool prime(int x)
{
int i;
for(i = 2; i * i <= x; i++)
{
if(x % i == 0)
return false;
}
return true;
}
int main ()
{
int count = 0;
for(int i = 2; i <= 40000; i++)
{
if(prime(i))
{
Prime[++count] = i;
C[i] = 1;
}
}
int t, n;
cin >> t;
while(t--)
{
int z;
int num = 0;
cin >> n;
for(int i = 1; i <= n; i++)
{
if(Prime[i] > n)
break;
else
{
for(int j = i; j <= n; j++)
{
if(Prime[j] + Prime[i] > n)
break;
else
{
z = n - Prime[i] - Prime[j];
if(C[z] == 1 && z >= Prime[j])
{
num++;
}
}
}
}
}
cout << num << endl;
}
}
题目描述:
有n个顶点,每个顶点有自己的坐标(Xi,Yi,Zi),现在想把这n个顶点连接起来,已知连接两个顶点u和v的花费是 MIN(|Xu−Xv|,|Yu−Yv|,|Zu−Zv|)。现在,请花费最小的代价把这些点连接起来
输入:
第一行输入一个整数n (1≤n≤2∗105)
接下来n行,第i行包含3个整数Xi,Yi和Zi(|Xi|,|Yi|,|Zi|≤109),代表第i个点的坐标,数据保证不会有任意两个点的坐标相同
输出:
输出最小代价。
样例输入:
3
1 1 1
2 3 4
5 5 5
样例输出:
2
自己太菜,提醒过是最小生成树的算法,但是完全不会应用到上面。下次补上(记!!!!!!)
题目描述:
众所周知。固定长度的K进制可以表达的范围是固定的。小O最近就迷上了进制数,但是小O不喜欢进制数里相邻两位的数字太相近。因此小O想知道满足这样条件的进制数有多少。
首先,这个进制数必须是N位长度的K进制数,并且满足任意相邻的两位数字之差的绝对值大于C
输入:
首先一个数字t(1≤10≤t),代表多组输入
接下来t行,每行3个数字N,K,C(1≤N≤103, 2≤K≤103, 0≤C≤min(K−2,10))
输出:
对于每组数据输出一个整数,这个数可能十分大,将结果对109+7取模。
样例输入:
1
2 4 1
样例输出:
4
没怎么学过的动态规划。学了再补
题目描述:
给出一个整数n,将其分解为若干个正整数之和,使得这些正整数的乘积x最大,
然后请你计算1nmod x。(测试数据保证n与x互质)
输入:
一个整数N(5≤n≤59)
输出:
1nmod x的值
样例输入:
7
样例输出:
7
自己的题解:求乘积x是有规律的,如下:
可见我们只需一直循环这个n就行,n跳出来的条件为(n<4)
最后只需要乘以循环i的3加上最后的数就行。
取模的内容用上了扩展欧几里得算法(虽然我还得再看一下(ㄒoㄒ))
以下是代码:
#include
using namespace std;
#define ll long long
void ex_gcd(ll a,ll b,ll &x,ll &y){
if(!b)
{
x=1,y=0; return ;
}
ex_gcd(b,a%b,x,y);
ll t=x; x=y,y=t-(a/b)*y;
}
inline ll inv(ll a,ll mod){
ll inv_a,y;
ex_gcd(a,mod,inv_a,y);
return (inv_a%mod+mod)%mod;
}
int main()
{
int n;
ll mod = 1;
ll sum = 0;
cin >> n;
int i = n;
while (n>4) {
n = n - 3;
mod = mod * 3;
}
mod = mod * n;
sum = inv(i,mod);
cout << sum << endl;
return 0;
}
题目描述:
现在n个数m个操作,初始化时每个数都是0,现有两个操作
1 L R将[L,R]区间内的数反转,将1变成0,0变成1
2 X 查询第x个数是0还是1
输入:
第一行输入n和m。(1≤n≤105,1≤m≤105)
接下来m行,每行的第一个数t表示操作的类型
若t = 1,则接下来有两个数L和R,表示将[L,R]区间的数反转
若t = 2,则接下来有一个数i,表示第i个数的值。(1≤L,R,i≤n)
输出:
对于每一个t=2的操作,输出对应的答案。
样例输入:
20 10
1 1 10
2 6
2 12
1 5 12
2 6
2 15
1 6 16
1 11 17
2 12
2 6
样例输出:
1
0
0
0
1
1
自己的题解:一开始用了两个for循环,时间超时。后来学了一下线段树。区间赋值和区间查找就很快了
以下是代码:
#include
using namespace std;
const int MAX = 100004;
int Lazy[MAX * 4];
int Tree[MAX * 4];
void PushDown(int root)
{
Lazy[root * 2] = Lazy[root] + Lazy[root * 2];
Lazy[root * 2 + 1] = Lazy[root] + Lazy[root * 2 + 1];
Tree[root * 2] = Tree[root * 2] + Lazy[root];
Tree[root * 2 + 1] = Tree[root * 2 + 1] + Lazy[root];
Lazy[root] = 0;
}
void query(int root, int nstart, int nend, int x)
{
int Z;
if(x < nstart || x > nend)
return;
if(nstart == nend)
{
if(Tree[root] %2 != 0)
cout << "1" << endl;
else
cout << "0" << endl;
return;
}
if(Lazy[root])
PushDown(root);
int mid = (nstart + nend)/2;
if(mid >= x)
query(root*2, nstart, mid, x);
if(mid < x)
query(root*2+1, mid+1, nend, x);
}
void update(int root, int nstart, int nend, int l, int r)
{
if(l > nend || r < nstart)
return;
if(l <= nstart && r >= nend)
{
Lazy[root]++;
Tree[root]++;
return;
}
if(Lazy[root])
PushDown(root);
int mid = (nstart + nend)/2;
if(mid >= l)
update(root*2, nstart, mid, l, r);
if(mid < r)
update(root*2+1, mid+1, nend, l, r);
}
int main()
{
int n, m, X, L, R, Z, P;
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
cin >> X;
if(X == 1)
{
cin >> L >> R;
if(L > R)
{
int z = L;
L = R;
R = z;
}
update(1, 1, n, L, R);
}
if(X == 2)
{
cin >> P;
query(1, 1, n, P);
}
}
return 0;
}
题目描述:
有n个人,现在有一个聚会,每个人都可以选择参加或者不参加。而参加的人中每个人要么只去送礼物,要么只接受礼物。不存在全部都接受礼物或者全部都送礼物的情况(这样要么没人送礼物,要么没人接受礼物了)。问有多少中情况?
输入:
第一行输出n,表示总共的人数。(1≤n≤105)
输出:
输出总共的数量。由于数字很大,对10的9次方+7取模。
样例输入:
3
样例输出:
12
自己的题解:公式写出来可以展开为幂指数向剪。以为这里幂太大,可以采用快速幂。
以下是代码:
#include
#define LL long long
using namespace std;
const long long int mod=1e9+7;
long long poww(long long int a, long long int b) //快速幂
{
long long int sum = 1;
while(b)
{
if(b&1)
{
sum = sum % mod * a % mod;
}
a = a % mod * a % mod;
b >>= 1;
}
return sum;
}
int main()
{
int n;
cin >> n;
//while(cin >> n)
// {
long long int A = 0;
long long int B = 0;
long long int SUM = 0;
A = poww(3,n);
B = poww(2,n + 1);
//cout << A << " " << B << endl;
if(A < B)
SUM = (A + mod - B) % mod + 1;
else
SUM = (A % mod - B % mod) + 1;
cout << SUM << endl;
//}
return 0;
}
题目描述:
现在有两个相同大小的地图,左上角为起点,右下角问终点。问是否存在同一条最短路径。最短距离一样,他们走的路径也一样。
输入:
第一行输入n和m。表示nm网络大小的地图。(1≤n,m≤500)
接下来2n行,每行m个字符。表示可以通,#表示不能通。
前n行是第一个地图,后n行是第二个地图。保证左上角和右下角都是
输出:
如果存在相同的最短路径。则输出YES,否则输出NO
样例输出:
NO
好像是找两个地图的最短路径,再把两个图和起来找最短路径。但我还没写出来