比赛时写了B/F/G
这两天应该会补题目
题目链接
比赛:
- B dp+小优化
- F 博弈论
- G 找规律+快速幂
补题:
- A 二分+容斥原理
- D 快速幂(有除法)
- K 竟然还是快速幂?
- J 树状数组
B
Description
做完了辣么多的数学题,qwb好好睡了一觉。但是他做了一个梦:
有一个n*m的矩阵,qwb在这个矩阵的左上角(1,1),终点在右下角(n,m)。
每个格子中有小钱钱,也可能没有,还有可能是要交过路费的,并且行走方向必须是靠近终点的方向。
往下走一次只能走一格,往右走一次可以走一格也可以走到当前列数的倍数格。
比如当前格子是(x,y),那么可以移动到(x+1,y),(x,y+1)或者(x,y*k),其中k>1。
qwb希望找到一种走法,使得到达右下角时他能够有最多的小钱钱。
你能帮助他吗?
Input
第一行是测试例数量 T (T<=100),接下来是T组测试数据。
每组测试数据的第一行是两个整数n,m,分别表示行数和列数(1<=n<=20,1<=m<=10000);
接下去给你一个n*m的矩阵,每个格子里有一个数字 k (-100<=k<=100)代表小钱钱的数量。 ∑nm<=3,000,000
Output
每组数据一行,输出L先生能够获得小钱钱的最大值(可能为负数)。
Sample Input
1
3 8
9 10 10 10 10 -10 10 10
10 -11 -1 0 2 11 10 -20
-11 -11 10 11 2 10 -10 -10
Sample Output
52
思路: dp啊,tle一发,考虑到还有倍数可以跳,所以我把m的约数处理了一下,比赛完发现其实是可以刷表的。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
int data[25][10005];
int dp[25][10005];
const int inf = 0x3f3f3f3f;
vector<int> v[10005];
int main()
{
int t;
scanf("%d",&t);
for(int i = 2; i <= 10000; i++)
{
for(int j = 1; j < i; j++)
if(i%j == 0) v[i].push_back(j);
}
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n ; i++)
for(int j = 1; j <= m ; j++)
{
scanf("%d",&data[i][j]);
dp[i][j] = -inf;
}
for(int i = 0; i <= m; i++) dp[0][i] = -inf;
for(int j = 0; j <= n; j++) dp[j][0] = -inf;
dp[1][1] = data[1][1];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
int res = -inf;
if(i==1&&j==1) res = data[i][j];
res = max(res,dp[i-1][j] + data[i][j]);
res = max(res,dp[i][j-1]+ data[i][j]);
int si = v[j].size();
for(int k = 0; k < si; k++)
res = max(res,dp[i][v[j][k]]+data[i][j]);
dp[i][j] = res;
}
}
printf("%d\n",dp[n][m]);
}
return 0;
}
F
Description
qwb has a lot of coins. One day, he decides to play a game with his friend using these coins. He first puts some of his coins into M piles, each of which is composed of Ni (1<=i<=M) coins. Then, the two players play the coin game in turns. Every step, one can remove one or more coins from only one pile. The winner is the one who removes the last coin.
Then comes the question: How many different ways the first player can do that will ensure him win the game?
Input
Input contains multiple test cases till the end of file. Each test case starts with a number M (1 <= M<= 1000) meaning the number of piles. The next line contains M integers Ni (1 <= Ni <= 1e9, 1 <= i<= M) indicating the number of coins in pile i.
Output
For each case, put the method count in one line.
If the first player can win the game, the method count is the number of different ways that he can do to ensure him win the game, otherwise zero.
Sample Input
3
1 2 3
1
1
Sample Output
0
1
思路:简单博弈论,其实博弈论基本没怎么写过,但是就记得一种操作,那就是xor,勉强记起全部xor为0的时候是必败态,所以尽量把必败态留给别人。
nim博弈。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
int data[1005];
int main()
{
int n;
while(~scanf("%d",&n))
{
int x;
scanf("%d",&data[0]);
int ans = data[0];
for(int i = 1; i < n ; i++)
{
int k;
scanf("%d",&k);
data[i] = k;
ans = (ans^k);
}
if(!ans)
printf("%d\n",ans);
else
{
int sum = 0;
for(int i = 0; i < n; i++)
{
int x = ans^data[i];
if(data[i] >= x) sum++;
}
printf("%d\n",sum);
}
}
return 0;
}
G
Description
某一天,qwb去WCfun面试,面试官问了他一个问题:把一个正整数n拆分成若干个正整数的和,请求出这些数乘积的最大值。
qwb比较猥琐,借故上厕所偷偷上网求助,聪明的你能帮助他吗?
Input
第一行为一个正整数T.(T<=100000)
接下来T行,每行一个正整数n(n<=1e9),意义如题目所述。
Output
每一行输出一个整数,表示乘积的最大值,由于答案可能很大,请将答案对109+7取模后输出。
Sample Input
2
2
5
Sample Output
2
6
思路: 这题。。卡了好久没思路,后来用dp的想法打了个小表,发现基本都是3*d[i-3],后来分析发现也对,尽量取*3,余下的取2,但是还有4的时候取*4。
接下来就是快速幂的过程了。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
#define ll long long
const int mod = 1e9+7;
ll data[10000000];
int d[3] = {2,3,4};
ll quickpow(ll b)
{
ll x = (b-2)/3,wh = (b-2)%3,ans = 1,ss = 3;
while(x)
{
if(x&1) ans = (ans*ss)%mod;
x = (x >> 1);
ss = (ss*ss)%mod;
}
ans = (ans * d[wh]) %mod;
return ans;
}
int main()
{
int t;
scanf("%d",&t);
data[1] = 1,data[2] = 2,data[3] = 3;data[4] = 4;
while(t--)
{
int n;
scanf("%d",&n);
if(n <= 4)
printf("%lld\n",data[n]);
else
{
ll ans = quickpow(n);
printf("%lld\n",ans);
}
}
return 0;
}
这个比赛不难啊,不知道为什么写题的时候基本就是没有自信,总感觉自己的想法有问题,在g题上花费了太长时间了。如果能早点打表出来估计规律也就看出来了。写的题还是太少了。
补题:
A: 二分
听了别人的思想,自己写反正完全没往二分上靠,一直觉得找循环节循环节循环节,自己的想法是在lcm(x,y,z)内找个数,然后看N有多少个lcm的个数,但是这种在lcm特别大的时候,时间复杂度也没降下去。
代码:
//容斥原理+二分
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
#define ll long long
int x,y,z;
ll N;
ll gcd(ll a,ll b)
{ return b?gcd(b,a%b):a; }
ll lcm(ll a,ll b)
{ return a/gcd(a,b)*b; }
bool check(ll mid)
{
ll numx = mid/x, numy = mid/y, numz = mid/z;
ll ans = numx + numy + numz - mid/lcm(x,y) - mid/lcm(x,z) - mid/lcm(y,z);
ll s = lcm(lcm(x,y),z);
if(s > 0) ans += mid/s;
if(N > mid-ans) return 1;
else return 0;
}
int main()
{
while(~scanf("%d%d%d%lld",&x,&y,&z,&N))
{
ll r = 1e18,l = 1;
while(l < r)
{
ll mid = (l+r)/2;
if(check(mid)) l = mid+1;
else r = mid;
}
printf("%lld\n",r);
}
return 0;
}
D
没打算自己能过的,结果手动打表出奇迹,推出了一个神奇的式子 1+1+3+9+27。。。
然后式子可以表示为
f[i]=(3n+1)/2∗(x+y)
又是一道快速幂,可以发现,这里有除法,所以搜到了一篇讲解:
http://blog.csdn.net/acdreamers/article/details/8220787
可以发现一个很厉害的式子:a/b%mod = a%(b*mod)/b
对于这题就可以在快速幂过程把1e8设成2*1e8。
听别人说推出来f[n] = f[n-1]*3 - (x+y)
这个式子是可以构造矩阵快速幂的,而且不需要考虑除法
构造的矩阵为:
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
#define ll long long
const ll mod = 1e8;
ll x,y,n;
ll quickpow(ll a,ll mod)
{
ll ans = 1,t = 3;
while(a)
{
if(a&1) ans = (ll)(ans*t)%mod;
a = (a>>1);
t = (ll)(t*t)%mod;
}
return ans;
}
int main()
{
while(~scanf("%lld%lld%lld",&x,&y,&n))
{
if(x==0 && y==0) {printf("0\n"); continue;}
ll k = (x%mod+y%mod)%mod;
ll pow3 = quickpow(n, mod*2);
ll ans = (pow3+1)/2;
ans = (ll)(ans * k)%mod;
printf("%lld\n",ans);
}
return 0;
}
K
快速幂。。。。。。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define ll long long
ll x,y,n;
ll quickpow(ll n,ll y)
{
ll ans = 1,a = 10;
if(!n) return 1;
while(n)
{
if(n&1) ans = (ans*a)%y;
n = (n >> 1);
a = (a*a)%y;
}
return ans;
}
int main()
{
while(~scanf("%lld%lld%lld",&x,&y,&n))
{
ll ans = ((x%y)*quickpow(n-1,y))%y;
printf("%lld\n",ans*10/y);
}
return 0;
}
J
思路: 树状数组。 需要两个,一个记录num,一个记录收入总和。
需要考虑收入为0的时候,因为树状数组的下标是从1开始的。
因为改到后面很烦躁把所有的int都改成long long了。
这两天写线段树的进度有点慢。今天的效率也是低的不行。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 1e6+100;
ll c[maxn],a[maxn];
ll n;
ll lowbit(ll x) {return x&-x;}
void add(ll id)
{
for(ll i = id; i <= maxn; i += lowbit(i)) {c[i] += (id-1);a[i] += 1;}
}
ll q(ll id)
{
ll ans = 0;
for(ll i = id; i; i -= lowbit(i)) ans += c[i];
return ans;
}
ll q1(ll id)
{
ll ans = 0;
for(ll i = id; i; i -= lowbit(i)) ans += a[i];
return ans;
}
int main()
{
while(~scanf("%lld",&n))
{
memset(c,0,sizeof(c));
memset(a,0,sizeof(a));
ll a,b,x;
for(int i = 1; i <= n; i++)
{
scanf("%lld%lld",&a,&b);
if(!a) add(b+1);
else
{
scanf("%lld",&x);
b++,x++;
ll ans = q(x)-q(b-1), num = q1(x) - q1(b-1);
if(num == 0) {printf("zhizhiwuwu\n");continue;}
ans = (ll)(ans/num);
printf("%lld\n",ans);
}
}
printf("\n");
}
return 0;
}