山东省第八届acm大学程序设计竞赛山师选拔赛第二场
(声明:标题是自己取的,如果有语法错误的话与他人无关)
Problem_A(最近公共祖先)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int MAXN = 10010;
const int DEG = 20;
struct Edge
{
int to,next;
} edge[MAXN*2];
int head[MAXN],tot;
void addedge(int u,int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
int fa[MAXN][DEG];//fa[i][j]表示结点i的第2^j个祖先
int deg[MAXN];//深度数组
void BFS(int root)
{
queue<int>que;
deg[root] = 0;
fa[root][0] = root;
que.push(root);
while(!que.empty())
{
int tmp = que.front();
que.pop();
for(int i = 1; i < DEG; i++) fa[tmp][i] = fa[fa[tmp][i-1]][i-1];
for(int i = head[tmp]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(v == fa[tmp][0])continue;
deg[v] = deg[tmp] + 1;
fa[v][0] = tmp;
que.push(v);
}
}
}
int LCA(int u,int v)
{
if(deg[u] > deg[v])swap(u,v);
int hu = deg[u], hv = deg[v];
int tu = u, tv = v;
for(int det = hv-hu, i = 0; det ; det>>=1, i++) if(det&1) tv = fa[tv][i];
if(tu == tv)return tu;
for(int i = DEG-1; i >= 0; i--)
{
if(fa[tu][i] == fa[tv][i]) continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][0];
}
bool flag[MAXN];
int main()
{
int T;
int n;
int u,v;
while(~scanf("%d",&n))
{
init();
memset(fa,0,sizeof(fa));
memset(deg,0,sizeof(deg));
memset(flag,false,sizeof(flag));
for(int i = 1; i < n; i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
flag[v] = true;
}
int root;
for(int i = 1; i <= n; i++) if(!flag[i])
{
root = i;
break;
}
BFS(root);
scanf("%d%d",&u,&v);
if(LCA(u,v))
printf("%d\n",LCA(u,v));
else
puts("I am so bad.");
}
return 0;
}
Problem_B(矩阵快速幂)
题意:对任意的(1+sqrt(2)) ^n我们是否能找到对应的m使得上式化简为sqrt(m) +sqrt(m-1),如果有,请输出m%(1e9+7)的值,没有输出"I want to talk a joke."
思路:先写几个数字看看什么情况。化简后会看出(整数部分)系数为1、3、7、17……带有sqrt(2)的部分的系数为1、2、5、12……后者数值等于它上一个数的这两部分的系数之和(2=1+1,5=3+2,12=7+5……),前者的数值等于和他一个数的sqrt(2)的部分的系数+它上一个数的sqrt(2)的部分的系数(3=2+1,7=5+2,17=12+5……)。根据这个规律构造一个四阶矩阵,然后套上模板就可以了。注意每次乘完都要%MOD一下,防止溢出。
关于输出:如果n是奇数的话,就输出(整数部分)系数的^2,否则输出它的^2+1(自己推一推便知道了。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 10010;
const int MOD = 1e9 + 7;
const int ch = 4;
struct matrix
{
long long mat[ch][ch];
};
matrix multiply(matrix a, matrix b) //构造矩阵乘法
{
int i, j, k;
matrix t;
memset(t.mat, 0, sizeof(t. mat));
for(i = 0; i < ch; ++i)
for(j = 0; j < ch; ++j)
for(k = 0; k < ch; ++k)
t.mat[i][j] = (t.mat[i][j] + a.mat[i][k]*b.mat[k][j]%MOD) % MOD;
return t;
}
long long fibonacci(long long n)
{
long long tem = n;
n--;
matrix base, ans;
memset(base.mat, 0, sizeof(base.mat));
memset(ans.mat, 0, sizeof(ans.mat));
base.mat[2][0] = base.mat[2][2] = base.mat[3][1] = base.mat[3][2] = base.mat[3][3] = 1;
base.mat[2][3] = 2;
ans.mat[0][1] = ans.mat[0][0] = 1;
ans.mat[0][2] = 2;
ans.mat[0][3] = 3;
while(n)
{
if(n & 1) ans = multiply(ans, base);
base = multiply(base, base);
n >>= 1;
}
//cout << tem << endl;
if(!(tem&1)) return ans.mat[0][1]*ans.mat[0][1]%MOD;
return (ans.mat[0][1]*ans.mat[0][1]+1)%MOD;
}
int main()
{
long long n;
while(cin >> n)
cout << fibonacci(n) << endl;
return 0;
}
Problem_C(签到题)
题意:给定1-n个城市,你处在x位置,求出满足|x-i|<=r的正整数i的可能取到的数。
思路:因为给定了n的范围,所以一共会有4种情况,即左越界,左不越界,右越界,右不越界。4个if,轻松搞定。(我这个人比较懒,用一个for遍历了一遍,符合条件就让ans++,否则不作操作。比较而言当然是前者时间复杂度更低了,比赛时当然是要选择最优解法。
AC代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
int t;
double n, r, x; //用int就行
scanf("%d",&t);
while(t--)
{
int ans = 0;
scanf("%lf%lf%lf",&n,&r,&x);
for(int i = 1; i <= n; ++i)
{
if(fabs(x-i) <= r) ans++;
}
cout << ans << endl;
}
return 0;
}
Problem_D(线段树单点更新求和+期望公式)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 1<<16;
struct node
{
int l, r;
long long sum, sum2;
} tree[MAX<<2];
long long a[MAX];
long long sum, sum2;
void pushup(int rt)
{
tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum;
tree[rt].sum2 = tree[rt<<1].sum2 + tree[rt<<1|1].sum2;
}
void build(int l, int r, int rt)
{
tree[rt].l = l;
tree[rt].r = r;
tree[rt].sum = 0;
if(l == r)
{
tree[rt].sum = a[l];
tree[rt].sum2 = a[l]*a[l];
return ;
}
int mid = (tree[rt].l + tree[rt].r) >> 1;
//递归建树
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
pushup(rt);
}
void update(int x, int y, int rt)
{
//更新这个区间的值
if(tree[rt].l == tree[rt].r)
{
tree[rt].sum = y;
tree[rt].sum2 = y*y;
return ;
}
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(x <= mid) update(x, y, rt<<1);
else update(x, y, rt<<1|1);
pushup(rt);
}
void query(int x, int y, int rt)
{
if(tree[rt].l == x && tree[rt].r == y)
{
sum += tree[rt].sum;
sum2 += tree[rt].sum2;
return ;
}
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(y <= mid) query(x, y, rt<<1);
else if(x > mid) query(x, y, rt<<1|1);
else
{
query(x, mid, rt<<1);
query(mid+1, y, rt<<1|1);
}
}
int main()
{
int n, m;
while(~scanf("%d%d",&n,&m))
{
for(int i = 1; i <= n; ++i)
scanf("%lld",&a[i]);
build(1, n, 1);
while(m--)
{
int q;
scanf("%d",&q);
if(q == 1)
{
int pos, num;
scanf("%d%d",&pos,&num);
update(pos, num, 1);
}
else
{
int l, r;
sum = sum2 = 0;
scanf("%d%d",&l,&r);
query(l, r, 1);
long long ans = (r-l+1)*sum2 - sum*sum;
cout << ans << endl;
}
}
}
return 0;
}
Problem_E(最短路)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <sstream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAX = 510;
int n;
int adj[MAX][MAX];
int a[MAX], dis[MAX];
bool vis[MAX];
void init()
{
for(int i = 1; i <= 500; ++i)
{
for(int j = 1; j <= 500; ++j)
{
if(i == j) adj[i][j] = 0;
else adj[i][j] = INF;
}
}
}
void Dij()
{
int tem, minx;
memset(vis, false, sizeof(vis));
for(int i = 0; i <= n; ++i)
dis[i] = adj[1][i];
dis[1] = 0;
vis[1] = true;
for(int i = 1; i < n; ++i)
{
minx = INF;
for(int j = 1; j <= n; ++j)
{
if(!vis[j] && dis[j] < minx)
{
minx = dis[j];
tem = j;
}
}
if(minx == INF) break;
vis[tem] = 1;
for(int j = 1; j <= n; ++j)
{
if(!vis[j] && adj[tem][j] != INF && dis[tem] + adj[tem][j] <dis[j])
{
dis[j] = adj[tem][j] + dis[tem];
}
}
}
}
int main()
{
int t;
int item;
string stem, s;
while(~scanf("%d",&t))
{
init();
scanf("%d",&n);
getchar();
while(t--)
{
int cou = 0;
getline(cin, stem);
stringstream ss (stem);
while(ss >> item)
{
a[cou++] = item;
}
for(int i = 0; i < cou-1; ++i)
for(int j = i+1; j < cou; ++j)
{
adj[a[i]][a[j]] = 1;
}
}
Dij();
if(dis[n] == INF) cout << "-1" << endl;
else cout << dis[n]-1 << endl;
}
return 0;
}
Problem_F(思维)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 5e6 + 5;
int a[N];
int main()
{
int n;
bool flag;
int ans1, ans2, tem1, tem2;
while(~scanf("%d",&n))
{
flag = true;
ans1 = ans2 = -1;
tem1 = 0;
tem2 = -1;
for(int i = 0; i < n; ++i)
scanf("%d",&a[i]);
for(int i = 1; i < n; ++i)
{
if(a[i-1] == a[i])
{
if(tem2 - tem1 > ans2 - ans1)//更新答案
{
ans2 = tem2;
ans1 = tem1;
}
tem1 = tem2 = -1;
continue;
}
if(a[i-1] < a[i]) //上升
{
if(!flag)
{
if(tem2 - tem1 > ans2 - ans1)//更新答案
{
ans2 = tem2;
ans1 = tem1;
}
flag = 1;
tem1 = tem2 = -1;
}
if(tem1 == -1) tem1 = i-1;
//cout << i << "--" << tem1 << endl;
}
else
{
flag = 0;
if(tem1 != -1) tem2 = i;
}
if(tem2 - tem1 > ans2 - ans1)//更新答案
{
ans2 = tem2;
ans1 = tem1;
}
}
if(ans1 != -1 && ans2 != -1)
cout << ans1 << " " << ans2 << endl;
else
cout << "-1 -1" << endl;
}
return 0;
}
Problem_G(并查集)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int MAX = 1000;
int tot, area, peri, cou;
int f[MAX*MAX+5]; //记录是否联通
char mapa[MAX+5][MAX+5]; //存图
int dir[][2] = {0,1,0,-1,1,0,-1,0};
void init()
{
for(int i = 0; i <= MAX*MAX; ++i)
{
f[i] = i;
}
}
int getf(int v)
{
if(f[v] != v)
f[v] = getf(f[v]);
return f[v];
}
int merg(int u, int v)
{
int t1 = getf(u);
int t2 = getf(v);
/*cout << u << "--" << v << endl;
cout << t1 << "----" << t2 << endl << endl;*/
if(t1 != t2)
{
f[t2] = t1;
return 1; //与周围的岛屿之前不连通
}
return 0;
}
void solve(int x, int y)
{
int xx, yy;
for(int i = 0; i < 4; ++i)
{
xx = x + dir[i][0];
yy = y + dir[i][1];
if(xx < 0 || xx >= MAX || yy < 0 || yy > MAX) continue;
if(mapa[xx][yy] != '#') continue;
//cout << xx << "---" << yy << endl;
if(merg(x*MAX+y, xx*MAX+yy)) //联通成功,总岛屿数-1
{
tot--;
}
peri -= 2; //周围有一个岛屿的话,周长-2
}
}
int main()
{
int t;
int x, y;
while(~scanf("%d",&t))
{
init();
tot = area = peri = 0;
memset(mapa, 0, sizeof(mapa));
while(t--)
{
scanf("%d%d",&x,&y);
if(mapa[x][y] == '#')
{
cout << tot << " " << area << " " << peri << endl;
continue;
}
//先假设新加入的岛屿与其他都不连通
tot++;
area++;
peri += 4;
mapa[x][y] = '#';
//开始做减法
solve(x, y);
cout << tot << " " << area << " " << peri << endl;
}
}
return 0;
}
Problem_H(博弈+大数)
import java.math.BigInteger;
import java.math.BigDecimal;
import java.util.*;
public class Main {
public static void main(String[] args)
{
Scanner scanner = new Scanner(System.in);
int T;
BigInteger n;
BigInteger MOD = new BigInteger("5");
T = scanner.nextInt();
while((T--) > 0)
{
n = scanner.nextBigInteger();
if(n.mod(MOD).compareTo(BigInteger.ZERO) == 0)
System.out.println("chaochao");
else
System.out.println("huahua");
}
}
}
Problem_I(素数打表)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 1500005;
int a[MAX], tot;
bool vis[MAX];
void init()//筛法求a数组
{
memset(vis, false, sizeof(vis));
//vis[0] = vis[1] = true;
tot = 0;
int M = sqrt(MAX+0.5);
for(int i = 2; i <= M; ++i)
{
if(!vis[i])
{
a[tot++] = i;
for(int j = i*i; j <= MAX; j += i)
{
vis[j] = true;
}
}
}
}
int main()
{
init();
int t, n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i = 0; i < tot; ++i)
{
if(!vis[n+a[i]])
{
cout << n+a[i] << " " << a[i] << endl;
break;
}
}
}
return 0;
}
Problem_J(贪心)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 20005;
long long a[MAX];
int main()
{
int n;
long long ans;
while(~scanf("%d",&n))
{
ans = 0;
for(int i = 0; i < n; ++i)
{
scanf("%lld",&a[i]);
}
sort(a, a+n);
int tot = 1;
while(tot < n)
{
a[tot] = a[tot] + a[tot-1];
ans += a[tot];
for(int i = tot; i < n-1; ++i)
{
if(a[i] <= a[i+1]) break;
swap(a[i], a[i+1]);
}
tot++;
/*cout << " tot=" << tot << endl;
for(int i = 0; i < n; ++i)
cout << a[i] << " ";
cout << endl;*/
}
//cout << endl;
cout << ans << endl;
}
return 0;
}
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
struct cmp //自定义优先级
{
bool operator () (long long &a, long long &b) const
{
//最小值优先
return a > b;
}
};
int main()
{
int n;
long long ans, tem;
priority_queue<long long, vector<long long>, cmp >pq;
//因为priority_queue中有已经定义好的越小的整数优先级越大,所以可以直接调用
//priority_queue<long long, vector<long long>, greater<long long> >pq;
while(~scanf("%d",&n))
{
ans = 0;
while(!pq.empty()) pq.pop();
while(n--)
{
scanf("%lld",&tem);
pq.push(tem);
}
while(!pq.empty())
{
tem = pq.top();
pq.pop();
if(pq.empty()) break;
//cout << "tem=" << tem << endl;
tem += pq.top();
pq.pop();
//cout << "tem2=" << tem << endl;
ans += tem;
pq.push(tem);
}
cout << ans << endl;
}
return 0;
}
选拔赛结束了,虽然以排名比较靠前的成绩进了正式队,但是心里很是不甘,特别是这一次,很多能做的题都没做出来,还有一开始I的数据范围看错+打表打错,浪费了太多时间也影响了整体士气(我的锅。
还剩25天了,多学知识的同时也要修炼自己沉稳的性格,不管结果如何,不留遗憾就好。