第二次暑假集训

A - Zero Quantity Maximization

  • 题意:给一个a数组,一个b数组,取一个d,使得ai*d+bi=0,求d取何时值,等式成立的最多,输出等式成立的个数
  • 做法:这题需要用map存下每一个aid+bi=0的d值,求d出现次数最多的值即可,不过这里由于精度问题,所以选择存下一个二元组{a,b},对于ad+b=0成立,则对于adk+b*k=0也成立,所以这两种二元组的d是一样的,所以存下二元组{a/gcd(a,b),b/gcd(a,b)}即可对应一个d值,我们用map存下来即可
  • 特判:
    • ①:a=0,b=0时,对与任意d成立
    • ②:a=0,b≠0时,d不可能存在
    • ③:a≠0,b=0时,d为0,单独记下d的个数
    • ④:a为负数,就要a,b同时取个反,这样d是不变的

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX = 200005;
int a[MAX];
int ans; 
int s, t;
map<pair<int, int>, int> mp;
int gcd(int x, int y){
	return !(x%y) ? y : gcd(y, x % y);
}
int main(){
	int n; cin >> n;
	ans = s = t = 0;
	for(int i = 0; i < n; i++){
		cin >> a[i];
	}
	int temp;
	for(int i = 0; i < n; i++){
		cin >> temp;
		if(!temp && !a[i]) s++;
		else if(!temp) t++;
		else if(!a[i]) continue;
		else{
			if(a[i] < 0){
				a[i] = -a[i];
				temp = -temp;
			}
			int gd = gcd(a[i], abs(temp));
			pair<int, int> p = make_pair(a[i]/gd, temp/gd);
			mp[p]++;
			ans = max(ans, mp[p]); 
		}
	}
	ans = max(t, ans);
	cout << ans + s << endl;
	
	
	return 0;
}

B - Neighbor Grid

  • 题意:给你一个n*m矩阵,要求对于每一个元素可以加一个数,使得该位置相邻的不为0的个数恰好等于该位置的数值
  • 做法:构造法,矩阵的边上除了四个角都为3,四个角为2,其他数字都为4是最好情况,也是容错最高的情况,去对比一下原矩阵是否比要求矩阵大即可

代码

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int t,n,m;
    cin >> t;
    while(t--)
    {
        cin >> n >> m;
        int arr[n+5][m+5] = {0},ans = 1;
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= m;j++)
            {
                cin >> arr[i][j];
                if(arr[i][j] > 4) ans = 0;
                if((i == 1 && j == 1) || (i == 1 && j == m) || (i == n && j == 1) || (i == n && j == m))
                {
                    if(arr[i][j] > 2) 
                        ans = 0;
                }
                else if(i == 1 || j == 1 || i == n || j == m)
                {
                    if(arr[i][j] > 3) 
                        ans = 0;
                }
            }
        if(ans)
        {
            cout << "YES" << endl;
            for(int i = 1;i <= n;i++)
            {
                for(int j = 1;j <= m;j++)
                {
                    if((i == 1 && j == 1) || (i == 1 && j == m) || (i == n && j == 1) || (i == n && j == m)) cout << 2 << " ";
                    else if(i == 1 || j == 1 || i == n || j == m) cout << 3 << " ";
                    else cout << 4 << " ";
                }
                cout << endl;
            }
        }
        else
            cout << "NO" << endl;
    }
}

C - New Year Parties

  • 题意:给你n个数,每一个数字都可以加1,减1,或者不变,问最后不同的数字最多有多少,最少有多少
  • 做法:
    • 最小:由于每一个数字可变范围是2,所以两个数字是否可以变为一个数字,只要判断一下是否能用一个长度为2的区间同时包围这两个数字即可,所以最小个数只要用贪心,先排序。然后就是一个用最少长度为2个区间包括所有数字的求解了
    • 最大:每一个数字有两种走法,所以我们只要遍历一下所有数字,过程中判断一下该数字-1是否已经有过了,如果没有就让该数字减1,如果有过了,就判断一下该数有没有过,最后判断一下该数+1有没有过。

代码

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+5;
int a[N];
bool st[N];
int main(){
    int n;
    cin >> n;
    for(int i=0;i<n;i++){
        cin >> a[i];
    }
    sort(a,a+n);
    int ans=0;
    int last=0;
    for(int i=0;i<n;i++){
        
        if(a[i]>last){
            last=a[i]+2;
            ans++;
        }
        if(!st[a[i]-1]){
            st[a[i]-1]=true;continue;
        }
        if(!st[a[i]]){
            st[a[i]]=true;continue;
        }
        if(!st[a[i]+1]){
            st[a[i]+1]=true;continue;
        }
    }
    cout << ans;
    ans=0;
    for(int i=0;i<=a[n-1]+1;i++){
        if(st[i])ans++;
    }
    cout << " " << ans; 
}

D - Water The Garden

  • 题意:给你n个位置,k个水龙头,一个水龙头可以浇水的范围是[pos-j+1,pos+j-1],pos为该水龙头的位置,j是打开水龙头经过的时间,问你最少需要多久才能把所有位置浇水。
  • 做法:贪心,首先所有水龙头都要打开,求出每一个位置最早可能被浇水的时间,然后去所有位置的最大值即可

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

int a[1005],b[1005];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,k;scanf("%d%d",&n,&k);
        int ans = 0;
        memset(b,0x3f,sizeof(b));
        for(int i = 1;i <= k;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= k;j++)
            {
                b[i] = min(b[i],abs(i - a[j]) + 1);
            }
            ans = max(b[i],ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}

E - Doors Breaking and Repairing

  • 题意:给你n个数字,两个人一个人做把该数字减1~x的操作,一个人做把该数字加1 ~ y的操作,但是不能把已经为0的数字加y,一个数字最小为0.求最后为0的数字的个数
  • 做法:对于x>y的情况,肯定所有的数字最后都可以为0,如果x<=y,那么只能把一些小于等于x的数字变为0,同时另一个人肯定把这些数字加y,所以最后能是0的数字就是那些小于等于x的数字的一半,向上取整

代码

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int n,a,b;
    int ans=0;
    cin >> n >> a >> b;
    int num1=0;
    for(int i=0;i<n;i++){
        int v;
        cin >> v;
        if(v<=a)num1++;
    }
    if(a>b){
        cout << n;
    }else{
        cout << (num1+1)/2;
    }
}

F - SUM and REPLACE

  • 题意:给n个数字,有m次操作,一种是把[l,r]之间的数字都变为该数字的欧拉函数值,一个是求出[l,r]之间的所有数字和
  • 做法:用线段树来存下每一个数字,由于欧拉函数经过10次之后一定为1或者2,所以一个区间经过10次改变操作之后就不会再变化了,所以再进行的改变操作可以不进行了,这里用time数组存下每一个节点经过改变操作的次数。

代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N=300010;
const int MAX=1000005;
struct point{
    int l,r;
    ll v;
}tr[N*4];
ll num[MAX];
ll n,m;
ll f[N];
ll time_[4*N];
void init()
{
    for (int i = 1; i < MAX; i ++ )
        for (int j = i; j < MAX; j += i)
               num[j]++;
}
void pushup(ll u)
{	
	tr[u].v=tr[u<<1].v+tr[u<<1|1].v;
}
ll query(int u,int l,int r){
    ll v=0;
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u].v;
    ll mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)v+=query(u<<1,l,r);
    if(r>mid)v+=(v,query(u<<1|1,l,r));
    return v;
}
void build(int u,int l,int r){//建树
    tr[u]={l,r};
    if(l==r){
        tr[u].v=f[l];
        return;
    }
    int mid=l + r >> 1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(u);
}
void replace(int u,int l,int r){
    if(tr[u].l >= l && r >= tr[u].r)
    {
        if(time_[u] >= 10)
            return ;
        time_[u] ++;
    }
    if(tr[u].r==tr[u].l){
        tr[u].v=num[tr[u].v];
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) replace(u << 1, l, r);
    if(r>mid)replace(u << 1 | 1, l, r);
    pushup(u);
}
int main(){
    cin >> n >> m;
    init();
    for(int i=1;i<=n;i++)cin >> f[i];
    build(1,1,n);
    while(m--){
        int a,l,r;
        cin >> a >> l >> r;
        if(a==1){
           replace(1,l,r);
        }else{
            cout << query(1,l,r) << "\n";
        }
    }
}

G - Solve The Maze

  • 题意:给你一个n*m的矩阵,其中‘.’表示空位置,'G’表示好人,‘B’表示坏人,’#'表示墙,问最后是否可以通过再‘.’的位置放上墙使得好人都可以走到(n,m)点,坏人走不到该点
  • 做法:首先我们贪心的把所有坏人的四周都填上墙,因为这样是可以再围住一个坏人的前提下给好人最大行动空间的做法,如果一个坏人周围有好人,那么必然无解,再围住所有坏人之后,再从(n,m)点做bfs,看是否能走到好人的位置,判断一下是否所有好人都可以走到即可

代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N=55;
char f[N][N];
struct node{
    int x,y;
};
int n,m;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
bool vis[N][N];
bool st[N][N];
bool check(int a,int b){
    for(int i=0;i<4;i++){
        int x=a+dx[i];
        int y=b+dy[i];
        if(x>=0&&y>=0&&x<n&&y<m){
            if(f[x][y]=='G')return true;
        }
    }
    return false;
}
bool bfs(){
    queue<node> q;
    memset(st,false,sizeof st);
    q.push({n-1,m-1});
    st[n-1][m-1]=true;
    while(!q.empty()){
        node temp=q.front();
        q.pop();
        int a=temp.x;
        int b=temp.y;
        for(int i=0;i<4;i++){
            int x=a+dx[i];
            int y=b+dy[i];
            if(x>=0&&y>=0&&x<n&&y<m&&f[x][y]!='#'&&!st[x][y]){
                st[x][y]=true;
                if(f[x][y]=='G')vis[x][y]=true;
                q.push({x,y});
            }
        }
    }
}
int main(){
    int T;
    cin >> T;
    while(T--){
        bool flag=true;
        cin >> n >> m;
        int numg=0;
        for(int i=0;i<n;i++)cin >> f[i];
        memset(vis,false,sizeof vis);
        for(int i=0;i<n&&flag;i++){
            for(int j=0;j<m&&flag;j++){
                if(f[i][j]=='G')numg++;
                if(f[i][j]=='B'){
                    if(check(i,j)){
                        flag=false;
                        break;
                    }
                    for(int k=0;k<4;k++){
                        int x=i+dx[k];
                        int y=j+dy[k];
                        if(x>=0&&y>=0&&x<n&&y<m){
                            if(f[x][y]=='.')f[x][y]='#';
                        }
                    }
                }
            }
        }
        if(f[n-1][m-1]=='#'&&numg)flag=false;
        if(flag)bfs();
        for(int i=0;i<n&&flag;i++){
            for(int j=0;j<m&&flag;j++){
                if(f[i][j]=='G'){
                    if(!vis[i][j]){
                        flag=false;
                        break;
                    }
                }
            }
        }
        if(!flag){
            cout << "No\n";
        }else{
            cout << "Yes\n";
        }
    }
}

H - Segments

  • 题意:给你n长度区间,求要把该区间所有的连续区间不重叠的画出来需要几个图层
  • 做法:打标找规律,最后规律:f[i]=f[i-2]+i;

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int f[150];
int main()
{
    f[1]=1;
    f[2]=2;
    f[3]=4;
    int n;    
    cin >> n;
    
    for(int i=4; i<=n; i++) f[i]=f[i-2]+i;
    cout << f[n];
    return 0;
}

I - Tea Party

  • 题意:给你n个杯子,w毫升水,满足已下条件
    • ①:所有杯子至少有半杯水
    • ②:所有w的水都要放入杯子中
    • ③:杯子里水都是整数毫升
    • ④:大杯子的水不能比小杯子水少
  • 做法:贪心,先把所有杯子里装一半的水,如果不能满足,就输出-1,再从大到小去把每一个杯子倒满,直到没有水位置即可

代码

#include <cstring>
#include <algorithm>
#include <iostream>
#include <map>
const int N=120;
struct node{
    int v;
    int pos;
}a[N];

using namespace std;
int w;
int n;
bool cmp(node x,node y){
    return x.v<y.v;
}
int main(){
    map<int,int> ans;
    cin >> n >> w;
    for(int i=0;i<n;i++){
        cin >> a[i].v;
        a[i].pos=i;
    }
    sort(a,a+n,cmp);
    for(int i=0;i<n;i++){
        ans[a[i].pos]=(a[i].v+1)/2;
        w-=ans[a[i].pos];
    }
    if(w<0){
        cout << -1;
        return 0;
    }
    int idx=n-1;
    while(w>0){
        if(a[idx].v-ans[a[idx].pos]<=w){
            w-=a[idx].v-ans[a[idx].pos];
            ans[a[idx].pos]=a[idx].v;
            idx--;
        }else{
            ans[a[idx].pos]+=w;
            w=0;
        }
    }
    for(int i=0;i<n;i++){
        cout << ans[i] << " ";
    }
}

J - Lakes in Berland

  • 题意:给你n*m的矩阵,‘.’表示湖泊,’ * '表示陆地,求最后留下k个湖泊要填的最少面积的湖泊的最后矩阵是多少,其中边缘的湖泊不算湖泊
  • 做法:dfs,求出所有不在边缘的湖泊,排序去前k个即可

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
char maps[55][55];
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
bool vis[55][55];
 
struct node
{
    int num;
    int x, y;
};
node pla[3000];
 
int cmp(node a, node b)
{
    return a.num < b.num;
}
 
int num;
bool flag;
int n, m, k;
 
void DFS(int x, int y)
{
    if (vis[x][y] || maps[x][y] == '*')
        return;
    printf("%d %d\n", x+1, y+1);
    num++;
    vis[x][y] = true;
 
    for (int i = 0; i < 4; ++i)
    {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if (nx < 0 || nx > n || ny < 0 || ny > m)///排除掉边界,即海边
        {
            num = 0;
            flag = false;
            continue;
        }
        if (maps[nx][ny] == '*' || vis[nx][ny])
            continue;
        DFS(nx, ny);
    }
}
 
void Fill(int x, int y)
{
    maps[x][y] = '*';
    for (int i = 0; i < 4; ++i)
    {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if (maps[nx][ny] == '*')
            continue;
        Fill(nx, ny);
    }
}
 
int main()
{
    scanf("%d %d %d", &n ,&m, &k);
    int cnt = 0;
    int ans = 0;
 
    memset(vis, false, sizeof(vis));
    for (int i = 0; i < n; ++i)
        scanf("%s", maps[i]);
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < m; ++j)
        {
            num = 0;
            flag = true;
            if (!vis[i][j] && maps[i][j] == '.')
            {
                DFS(i, j);
                if (flag)
                {
                    pla[cnt].num = num;
                    pla[cnt].x = i;
                    pla[cnt].y = j;
                    cnt++;
                }
            }
        }
    }
    sort(pla, pla + cnt, cmp);
    for (int i = 0; i < cnt - k; ++i)
        ans += pla[i].num;
    printf("%d\n", ans);
 
    for (int i = 0; i < cnt - k; ++i)
        Fill(pla[i].x, pla[i].y);
    for (int i = 0; i < n; ++i)
        printf("%s\n", maps[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值