2021NUC_ACM新生赛
总之就是痛心疾
A.中北折磨王
题目描述
输入描述
输出描述
样例输入
98 63
样例输出
7
思路
不知道各位在做的时候有没有一种感觉, 和熟知的辗转相除法即最大公约数
很像, 实际上的确是, 名字为更相减损法
, 考验细节的时候到了
最大公约数求解还有一点就是gcd(a, b) == gcd(b, a % b)
, 其名为欧几里得算法
代码
#include <iostream>
using namespace std;
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
int x, y;
cin >> x >> y;
cout << gcd(x, y) << endl;
return 0;
}
B.买橘子(未完成)
题目描述
输入描述
输出描述
样例输入
5 6
1 2 3 4 6
1
2
3
4
5
1
样例输出
0
1
3
5
6
2
思路
据说是莫比乌斯反演
代码
C.数论小王子
题目描述
输入描述
输出描述
样例输入
5 4
1 1 1 1 1
1 2 3 1
3 2 5
2 2 4 3
3 2 5
样例输出
0
5
提示
思路
裸的线段树了, 但是还是有很多实现细节
代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, m;
LL w[N];
LL q1,q2;
struct Node {
int l, r;
LL sum1, sum2;
LL add, mul;
}tr[N * 4];
void build(int i, int l, int r);
void query(int i, int l, int r);
void update(int u, int l, int r, LL c, int type);
void pushup(int i);
void pushup(Node& u, Node& l, Node& r);
void pushdown(int u, int len);
void pushdown(Node& u, Node& l, Node& r, int len);
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> w[i];
build(1, 1, n);
while (m --) {
int op, l, r;
LL c;
cin >> op;
if (op == 3) {
cin >> l >> r;
q1 = q2 = 0;
query(1, l ,r);
int cnt = r - l + 1;
double sumx = q1 * 1.0 / cnt;
double t = (q2 - 2 * sumx * q1 + sumx * sumx * cnt) / cnt;
//printf("%d %d %.3lf %d ", q2, q1, sumx, cnt);
printf("%.0lf\n", t);
}
else {
cin >> l >> r >> c;
if (op == 1) update(1, l, r, c, 1);
else update(1, l, r, c, 2);
}
}
return 0;
}
void update(int u, int l, int r, LL c, int type) {
if(tr[u].l >= l && tr[u].r <= r){
if(type == 2){
tr[u].mul *= c;
tr[u].add *= c;
tr[u].sum1 *= c;
tr[u].sum2 *= c * c;
}else{
tr[u].add += c;
tr[u].sum2 += 2 * c * tr[u].sum1 + c * c * (tr[u].r - tr[u].l + 1);
tr[u].sum1 += c * (tr[u].r - tr[u].l + 1);
}
return ;
}
pushdown(u, tr[u].r - tr[u].l + 1);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) update(u << 1, l, r, c, type);
if(r > mid) update(u << 1 | 1, l, r, c, type);
pushup(u);
}
void query(int u, int l, int r) {
if(tr[u].l >= l && tr[u].r <= r){
q1 += tr[u].sum1;
q2 += tr[u].sum2;
return ;
}
int mid = (tr[u].l + tr[u].r) >> 1;
pushdown(u, tr[u].r - tr[u].l + 1);
if(l <= mid) query(u << 1, l, r);
if(r > mid) query(u << 1 | 1, l, r);
//pushup(rt);
}
void build(int i, int l, int r) {
tr[i] = { l, r, 0, 0, 0, 1 };
if(l == r){
tr[i].sum1 = w[l];
tr[i].sum2 = w[l] * w[l];
return ;
}
int mid = (l + r) >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
pushup(i);
}
void pushup(int i) {
pushup(tr[i], tr[i << 1], tr[i << 1 | 1]);
}
void pushup(Node& u, Node& l, Node& r) {
u.sum1 = l.sum1 + r.sum1;
u.sum2 = l.sum2 + r.sum2;
}
void pushdown(int u, int len) {
pushdown(tr[u], tr[u << 1], tr[u << 1 | 1], len);
}
void pushdown(Node& u, Node& l, Node& r, int len) {
r.mul *= u.mul;
l.mul *= u.mul;
l.add = u.add + l.add * u.mul;
r.add = u.add + r.add * u.mul;
l.sum2 = l.sum2 * u.mul * u.mul + 2 * u.add * l.sum1 + (len - (len >> 1)) * u.add * u.add;
r.sum2 = r.sum2 * u.mul * u.mul + 2 * u.add * r.sum1 + (len >> 1) * u.add * u.add;
l.sum1 = u.add * (len - (len >> 1)) + l.sum1 * u.mul;
r.sum1 = u.add * (len >> 1) + r.sum1 * u.mul;
u.mul = 1;
u.add = 0;
}
D.wsp的机器人(新加)
题目描述
输入描述
输出描述
样例输入
7 2
1 2 3 4 5 6 7
1 1
1 2
样例输出
1
3
思路
经典区间和, 利用前缀和
求解即可
注意会卡掉cin
代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, m;
LL f[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) scanf("%lld", f + i), f[i] += f[i - 1];
while (m --) {
int l, r;
scanf("%d%d", &l, &r);
printf("%lld\n", f[r] - f[l - 1]);
}
return 0;
}
E.找最短
题目描述
输入描述
输出描述
样例输入
14
bacbacbacbacba
样例输出
3
思路
和字符串有关, 那必然是KMP
了, 结论是n - next[n]
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6 + 10;
int n;
string str;
int ne[N];
int main() {
cin >> n;
cin >> str;
str = " " + str;
for (int i = 2, j = 0; i <= n; i ++) {
while (j && str[i] != str[j + 1]) j = ne[j];
if (str[i] == str[j + 1]) j ++;
ne[i] = j;
}
cout << n - ne[n] << endl;
return 0;
}
F.我要进ACM
题目描述
输入描述
输出描述
样例输入
3
样例输出
我要进ACM
我要进ACM
我要进ACM
思路
代码
#include <iostream>
using namespace std;
int main() {
int T;
cin >> T;
while (T --) {
puts("我要进ACM");
}
return 0;
}
G.小智的难题(感谢张舟学长的分享)
题目描述
输入描述
输出描述
样例输入
3 6 3
2 4
4 2
0 0
样例输出
28
思路
第一部分 n列,每一列大于等于1小于等于k,总数为m。转换为容斥模型,1<=x<=k, 总数为m,替换为 0<=y<=k-1,总数为m-n,
容斥枚举有多少个违反<=k-1的条件,具体可以参考博客:https://blog.csdn.net/codeswarrior/article/details/81906367
复杂度O(m)级别,因为某些客观条件将数据范围减小后,可能认为某些dp方法也可以通过(大概率过不去),这里给出未经测试的dp思路
dp[0] = 1;
for(int i=1;i<=n;i++)//枚举列
for(int j=m;j>=i;j--)//枚举当前用过的总座位数
for(int p=1;p<=min(k,i);p++)//枚举当前列用的座位数
dp[i] = (dp[i-p]+dp[i])%mod;
第二部分最直接的思路 dp(i,j)表示当前列红色权值和为i,蓝色权值和为j的方案数,时间复杂度O((5n)^2 * n)
进一步优化 dp( i )表示到当前列红色权值和减去蓝色权值和为i的方案数 时间复杂度O(10 * n^2)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char in[] = "0.in";
char out[] = "0.out";
const int N = 1e3+10,M = 11,mod = 1e9+7;
typedef long long ll;
int a[N],b[N],n,m,k;
int kmi(int a,int b){
int ans = 1;
while(b){
if(b&1) ans = 1ll*ans*a%mod;
b>>=1;
a = 1ll*a*a%mod;
}
return ans;
}
const int maxn = 1e6+10;
int fac[maxn],inv[maxn];
void init(){
fac[0] = inv[0] = 1;
for(int i=1;i<maxn;i++) fac[i]=1ll*fac[i-1]*i%mod;
for(int i=1;i<maxn;i++) inv[i]=1ll*inv[i-1]*kmi(i,mod-2)%mod;
}
int C(int n,int m){
if(n<0||m<0||n<m) return 0;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
const int maxm = 4e4+10,midm = 1.5e4;
int dp[N][maxm];
void cal()
{
memset(dp,0,sizeof dp);
cin>>n>>m>>k;
for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
if(m<n){
cout<<0<<endl;
return;
}
ll ans = 0;
for(int i=0,op=1;i<=n&&i*k<=m-n;i++,op*=-1){
ans = ((ans + 1ll*op*C(n,i)*C(m-1-i*k,n-1)%mod)%mod + mod)%mod;
//cout<<"--"<<1ll*C(n,i)*C(m-1-i*k,n-1)%mod<<endl;
}
dp[0][midm] = 1;
for(int i=1;i<=n;i++){
for(int j=-1e4;j<=1e4;j++){
dp[i][j+midm] = (1ll*dp[i][j+midm]+dp[i-1][j-a[i]+midm])%mod;
dp[i][j+midm] = (1ll*dp[i][j+midm]+dp[i-1][j+b[i]+midm])%mod;
}
}
cout<<1ll*ans*dp[n][midm]%mod<<endl;
}
int main()
{
init();
for(int i = 1; i < 10; i ++)
{
in[0] = char(i+'0');
out[0] = char(i+'0');
freopen(in, "r", stdin);
freopen(out, "w", stdout);
cal();
}
return 0;
}
H.clq找队友
题目描述
输入描述
输出描述
样例输入
5 4 1 3 5
1 2 1
2 3 1
3 4 1
4 5 1
样例输出
8
提示
思路
最短需要走的距离, 最短路
无疑了, 那么该如何走呢需要走去两个点并且走回原地
- 从起点走去A点, 然后去B点, 最后回起点
- 从起点走去A点并且回原点, 然后去B点并且回原点
- 还有就是只能去到一个点的时候, 这个情况下只需要去到一个点然后返回即可
取上面两种情况的最小值, 注意long long
问题
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;
const int N = 1e5 + 10, M = 2e6 + 10;
int n, m, S, F1, F2;
int h[N], e[M], ne[M], idx;
LL w[M];
LL d[N];
bool st[N];
void dijkstra(int u) {
memset(st, false, sizeof st);
for (int i = 1; i <= n; i ++) d[i] = 1e18;
d[u] = 0;
priority_queue<PII, vector<PII>, greater<PII> > q;
q.push({ 0, u });
while (q.size()) {
PII t = q.top();
q.pop();
if (st[t.y]) continue;
st[t.y] = true;
for (int i = h[t.y]; i != -1; i = ne[i]) {
int j = e[i];
if (d[j] > t.x + w[i]) {
d[j] = t.x + w[i];
q.push({ d[j], j });
}
}
}
}
void add(int a, int b, LL c) {
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx ++;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m >> S >> F1 >> F2;
for (int i = 0; i < m; i ++) {
int a, b;
LL c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
dijkstra(S);
LL a = d[F1], b = d[F2];
dijkstra(F1);
LL c = d[F2];
if (a >= 1e18 && b >= 1e18) puts("clq meng le");
else if (a >= 1e18) cout << b * 2 << endl;
else if (b >= 1e18) cout << a * 2 << endl;
else cout << min((a + b) * 2, a + b + c) << endl;
return 0;
}
I.暖男zsz
题目描述
输入描述
输出描述
样例输入
5
11011
样例输出
2
思路
一个人想要吃饭就必须拿到两只筷子, 即左右都必须有一只筷子, 即有两个连续的1
那么只需要统计连续的1的数量, 然后除以2就是答案, 因为是环形的, 不如将最开始的1算在最后一个1上面,
代码
#include <iostream>
#include <vector>
using namespace std;
int n;
string str;
vector<int> a;
int main() {
cin >> n;
cin >> str;
int i = 0;
while (i < n && str[i] == '1') i ++;
for (int j = i; j < n; j ++)
if (str[j] == '0') a.push_back(0);
else a.back() ++;
a.back() += i;
int ans = 0;
for (int i = 0; i < a.size(); i ++)
ans += a[i] / 2;
cout << ans << endl;
return 0;
}
G.谁说我不聪明
题目描述
输入描述
输出描述
样例输入
2 1
8 4
4 7
样例输出
ff
xy
ff
思路
威佐夫博弈
代码
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int x, y;
double t = (1 + sqrt(5)) / 2;
while (cin >> x >> y) {
if (x < y) swap(x, y);
if ((int)((x - y) * t) == y) puts("ff");
else puts("xy");
}
return 0;
}
K.别走泥坑
题目描述
输入描述
输出描述
样例输入
1 2 7
0 2
4 2
3 1
1 1
2 2
-1 1
-1 3
样例输出
11
思路
正常走迷宫即可, 主要是注意下标的hash
代码
#include <iostream>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e3 + 10;
int X, Y, n;
bool g[N][N];
int d[N][N];
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
void bfs() {
queue<PII> q;
q.push({ 0 + 500, 0 + 500 });
g[0 + 500][0 + 500] = true;
while (q.size()) {
PII t = q.front();
q.pop();
if (t.x == X && t.y == Y) break;
for (int i = 0; i < 4; i ++) {
int tx = t.x + dx[i], ty = t.y + dy[i];
if (tx < 0 || tx > 1000 || ty < 0 || ty > 1000) continue;
if (g[tx][ty]) continue;
g[tx][ty] = true;
d[tx][ty] = d[t.x][t.y] + 1;
q.push({ tx, ty });
}
}
}
int main() {
cin >> X >> Y >> n;
for (int i = 0; i < n; i ++) {
int a, b;
cin >> a >> b;
g[a + 500][b + 500] = true;
}
bfs();
cout << d[X + 500][Y + 500] << endl;
return 0;
}
L.最终圣战(感谢张佳豪学长的分享)
题目描述
输入描述
输出描述
样例输入
2
1 1 1 3 2 1
1 1 1 1 3 1
样例输出
2.9850
3.8264
思路
这道题是个数学题,具备三角函数知识与基本编程知识的就可以解掉,首先有三种情况
-
fabs(x-rx) <= rr
∠A的角度我们是可以求出来的,根据1/2absinC这个公式可以求出∠A的大小,然后再求小段圆弧所对应的角度,这个角度可以用大角减去上面角的角度得到,上面角已知两边并且是个直角三角形,根据公式得到小角的角度,对应的圆弧长就是小角+90度,总长度就是直线+这段圆弧的长度
-
fabs(x-rx) > rr && ry <= y
这种情况就是1/4段圆弧长 + 直线距离 -
fabs(x-rx) > rr && ry > y
有些人看到这里会疑惑,但是题目中说明一定有解,那么这个圆上的点一定可以走,而坐标,半径已知,我们不难求出总长度
代码
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define PI 3.1415926535
double len(double a, double b, double c, double d)
{
return sqrt((a - c) * (a - c) + (b - d) * (b - d));
}
double hu(double r, double jd)
{
return 2 * PI * r * (jd / 360.0);
}
int main()
{
// freopen("D:\\1.in", "r", stdin);
// freopen("D:\\9.out", "w", stdout);
int t;
cin >> t;
while (t--)
{
double rx, ry, r, x, y, jd, rr;
double ans = 0;
cin >> rx >> ry >> rr >> x >> y >> r;
if (fabs(rx - x) <= r)
{
/*
以下是根据S = 1/2absinC,以及反三角函数求角度,最后求弧长
*/
double S = rr * fabs(y - ry) / 2;
double sinC = S * 2 / (len(rx, ry, x, y) * rr);
double jd = asin(sinC) * 180.0 / PI + 90.0;
double jd1 = acos(r / len(rx, ry, x, y)) * 180.0 / PI;
ans = hu(rr, jd - jd1) + sqrt(len(rx, ry, x, y) * len(rx, ry, x, y) - r * r);
}
else
{
ans += hu(rr, 90.0);
// cout << ans << endl;
if (y >= ry)
{
if (x < rx)
ans += len(rx - rr, ry, x, y);
else
ans += len(rx + rr, ry, x, y);
}
else
{
ans += fabs(x - rx) - fabs(rr) - sqrt(r * r - (y - ry) * (y - ry)) + r;
}
}
printf("%.4lf\n", ans);
}
return 0;
}
M.简单的滑动解锁
题目描述
输入描述
输出描述
样例输入
2
0 1
1 1
0 1
0 0
样例输出
YES
思路
首先是肯定要枚举钥匙的四种旋转情况, 然后怎么判断是否重合呢?
题目中体现出来一个滑动
的概念, 那么对于钥匙的i行j列, 直到锁孔的i行j列都不能有阻拦
特别主要TLE
, 我们只需要枚举每一行最右边一个1即可
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010;
int n;
int a[N][N], b[N][N], c[N][N];
bool check() {
for (int i = 0; i < n; i ++)
for (int j = n - 1; j >= 0; j --)
if (a[i][j] == 1) {
for (int k = 0; k <= j; k ++)
if (b[i][k] == 1)
return false;
break;
}
return true;
}
void swap() {
int l = 0, r = n - 1;
for (int i = 0 ; i < n; i ++)
for (int j = 0; j < n; j ++) {
c[l][r] = a[i][j];
l ++;
if (l >= n) l = 0, r --;
}
memcpy(a, c, sizeof a);
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i ++)
for (int j = 0; j < n; j ++)
scanf("%d", &a[i][j]);
for (int i = 0; i < n; i ++)
for (int j = 0; j < n; j ++)
scanf("%d", &b[i][j]);
for (int i = 0; i < 4; i ++) {
if (check()) {
puts("YES");
return 0;
}
swap();
}
puts("NO");
return 0;
}
N.翻译官
题目描述
输入描述
输出描述
样例输入
6
abCaDb
样例输出
abcadb
思路
遍历字符串, 将大写字符转化为小写即可, A的ASCII码比a小32
代码
#include <iostream>
using namespace std;
int n;
string str;
int main() {
cin >> n;
cin >> str;
for (int i = 0; i < n; i ++)
if (str[i] < 'a')
str[i] += 32;
cout << str << endl;
return 0;
}
总结
题目总体难度不大, 思维量很小, 但是要求有一定的代码实现能力
题目还是没有开好, 在大家都做了好多的时候我一个也没有出来, 竟然会没有注意到中文乱码, 比赛慌神 果然还是练习太少了