目录
B - Build Roads
题目
用n-1条路链接n个城市,每个城市有自己的值ai,建路的时候会损失gcd(ai,aj)的值,求最少损失的值是多少
与之前不同的是,会生成随机数,而且数据很大
思路
一开始看这题觉得最小生成树肯定会超时,但又想不出别的办法,就。。
如果直接打表、或者尝试的话,就会发现,当n很大的时候,结果为n-1,(依据大概是因为素数的分布规律,随着数字增大,素数会增加,gcd就很容易出现1)
然后在打表之后,发现1000之前用最小生成树,1000之后就可以直接n-1输出
还要注意的是,L=R的时候,也是直接输出(n-1)*L
代码
#include <bits/stdc++.h>
#define maxx 201100
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
int n, L, R, a[maxx];
unsigned long long seed;
//create
unsigned long long xorshift()
{
unsigned long long x = seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return seed = x;
}
int gen()
{
return xorshift() % (R - L + 1) + L;
}
int gcd(int a, int b)
{
while (b)
{
int t = b;
b = a % b;
a = t;
}
return a;
}
//kruskal
int fa[1100];
int cnt = 1;
struct edge
{
int from, to, l=999999999;
} e[1100];
bool cmp(edge e1, edge e2)
{
return e1.l < e2.l;
}
void add(int x, int y, int l)
{
e[cnt].from = x;
e[cnt].to = y;
e[cnt].l = l;
cnt++;
}
int findd(int x)
{
if (fa[x] != x)
{
fa[x]=findd(fa[x]);
}
return fa[x];
}
int kruskal()
{
for (int i = 1; i <= n; ++i)
{
fa[i] = i;
}
sort(e+1,e+cnt+1,cmp);
int ans = 0;
int nn=0;
for (int i = 1; i <= cnt; ++i)
{
int x = e[i].from;
int y = e[i].to;
int ll = e[i].l;
int fx = findd(x);
int fy = findd(y);
if (fx!=fy)
{
fa[fy] = fx;
ans+=ll;
nn++;
}
if (nn==n-1)
break;
}
return ans;
}
int main()
{
scanf("%d%d%d%llu", &n, &L, &R, &seed);
if (L == R)
{
cout << 1ll*(n - 1) * L << endl;
return 0;
}
if (n > 1000)
{
cout<<n-1<<endl;
return 0;
}
for (int i = 1; i <= n; ++i)
{
a[i] = gen();
// cout << a[i] << endl;
}
for (int i = 1; i <= n; ++i)
{
for (int j = i + 1; j <= n; ++j)
{
int len = gcd(a[i], a[j]);
// cout<<len<<endl;
add(i,j,len);
add(j,i,len);
}
}
int ans = kruskal();
cout<<ans<<endl;
return 0;
}
C - Cat Virus
题目
构造一棵树,满足有k种染色方案
思路
一开始就觉得直接一串结点就可以满足题意了,但是题中n的范围远小于k的范围,肯定是不行的,所以就用最少的结点构造满足条件的树,然后找找规律:
从奇数到偶数:在根节点+根节点
从偶数到奇数:往上提一个与根节点最近的节点
感觉不看题解,想不到,呜呜呜呜
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL k;
int main(){
cin>>k;
LL fa=1,id=1,tmp=k,cnt=1;
while(tmp>3){
if(tmp&1){
tmp/=2;
cnt+=2;
}
else{
tmp--;
cnt++;
}
}
if(tmp==3) cnt++;//多加一个点
cout<<cnt<<endl;
while(k>3){
if(k&1){//奇数
k/=2;
id++;
cout<<fa<<" "<<id<<endl;//左
id++;
cout<<fa<<" "<<id<<endl;//右
fa=id;
}
else{//奇数
k--;
id++;
cout<<fa<<" "<<id<<endl;//右边
fa=id;
}
}
if(k==3){
id++;
cout<<fa<<" "<<id<<endl;//特判情况,连一个右边
}
}
D - Dyson Box
题目
给定一组坐标,放置方块,然后,或者把他们全部向下移动,或者全部向左移动,求每一次放置之后的图形的周长
思路
一开始想的方法是放一次求一次,找到每次求和的规律,但是后来超时了,一开始其实知道超时,但是不想去想别的思路了,所以浪费了一些时间。
找的规律应该是:每次放上去的周长与上一次周长的关系,这样就不用每次计算了~
代码
#include <bits/stdc++.h>
#define maxx 200100
#define inf 0x3f3f3f3f
using namespace std;
int N;
int vx[maxx], vy[maxx];
int main()
{
cin >> N;
int sum1 = 0, sum2 = 0;
for (int i = 0; i < N; ++i)
{
int x, y;
cin >> x >> y;
//vertical
vx[x]++;
if (vx[x] > vx[x - 1] && vx[x] > vx[x + 1])
{
if (vx[x] == 1)
sum1 += 4;
else
sum1 += 2;
}
if (vx[x] <= vx[x - 1] && vx[x] <= vx[x + 1])
{
if (vx[x] != 1)
sum1 -= 2;
}
if (vx[x] <= vx[x - 1] && vx[x] > vx[x + 1])
{
if (vx[x]==1)
sum1 += 2;
}
if (vx[x] > vx[x - 1] && vx[x] <= vx[x + 1])
{
if (vx[x]==1)
sum1 += 2;
}
cout<<sum1<<' ';
//horizontal
vy[y]++;
if (vy[y] > vy[y - 1] && vy[y] > vy[y + 1])
{
if (vy[y] == 1)
sum2 += 4;
else
sum2 += 2;
}
if (vy[y] <= vy[y - 1] && vy[y] <= vy[y + 1])
{
if (vy[y] != 1)
sum2 -= 2;
}
if (vy[y] <= vy[y - 1] && vy[y] > vy[y + 1])
{
if (vy[y]==1)
sum2 += 2;
}
if (vy[y] > vy[y - 1] && vy[y] <= vy[y + 1])
{
if (vy[y]==1)
sum2 += 2;
}
cout<<sum2<<endl;
}
return 0;
}
H - Adventurer’s Guild
题目
二维背包
思路
当时觉得自己没学过二维背包就没写。。其实不难的。。
代码
#include <bits/stdc++.h>
#define maxx 1100
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
int n, H, S;
ll f[maxx][maxx];
int main()
{
cin >> n >> H >> S;
ll ans = 0;
for (int i = 1; i <= n; ++i)
{
int h, s, w;
cin >> h >> s >> w;
for (int j = H; j > h; j--)
{
for (int k = S; k >= 0; k--)
{
if (j + k > h + s)
{
if (k < s) //stamina不够
{
f[j][k] = max(f[j][k], f[j - h - s + k][0] + w);
}
else
{
f[j][k] = max(f[j][k], f[j - h][k - s] + w);
}
}
}
}
}
cout << f[H][S] << endl;
return 0;
}
M - Matrix Problem
题目
矩阵C由矩阵A和B进行运算得到,给出矩阵C,求A,B
AB的运算方式:只有当AB中位置全为1时,C才为1
思路
因为A,B里的1要保证是连通的,一种构造方法是让A的最左边是1,B的最右边是1,然后A奇数行是1,B偶数行是1,就可以保证一定是连通的
主要思维题
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500 + 10;
int n,m;
int A[N][N], B[N][N], C[N][N];
int main()
{
scanf("%d%d",&n, &m);
for(int i = 0;i < n;++i){
getchar();
for(int j = 0;j < m;++j){
char ch = getchar();
C[i][j] = ch - '0';
}
}
for(int j = 0;j < n;++j){
A[j][0] = 1;
B[j][m-1] = 1;
}
for(int i = 0;i < n;++i){
if(i & 1){
for(int j = 1;j < m - 1;++j){
A[i][j] = 1;
}
}
else{
for(int j = 1;j < m - 1;++j){
B[i][j] = 1;
}
}
}
for(int i = 0;i < n;++i){
for(int j = 0;j < m;++j){
if(C[i][j] == 1){
A[i][j] = B[i][j] = 1;
}
}
}
for(int i = 0;i < n;++i){
for(int j = 0;j < m;++j){
printf("%d",A[i][j]);
}
}
for(int i = 0;i < n;++i){
for(int j = 0;j < m;++j){
printf("%d",B[i][j]);
}
}
return 0;
}
总结
第一次做5h的题目,感觉自己太菜了,啥也不会做。。
很多时间都在思考题目上,其实代码实现并不复杂
多多做题。。