codeforce 1400~1500(刷题记录)

12.29 ~ 12.30 复习期末累了 刷会算法题吧 1400 ~ 1500是我能做但是有时候会卡的题 关键是对题目信息的挖掘与转化 

1670C. Where is the Pizza?(并查集)

题目大意:给定俩个数组A,B再给一个数组D D数组中元素要么为A中元素 要么为B中元素 0表示任选 求能形成多少个从1~n的排列

例:

7

1 2 3 4 5 6 7

2 3 1 7 6 5 4

2 0 1 0 0 0 0

思路 对于第4个位置和第7个位置 这俩个位置 可以4 7 也可以7 4 第一次遇到4 7把他们加到一个集合当中 第二次遇到4 7判断一下他们是否在一个集合当中 若在 则ans*2(只在当前位置给的值是0的情况下操作)

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 100010,mod = 1e9 + 7;
typedef long long LL;

int a[N],b[N],p[N];
int n;

int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void solve()
{
    cin >> n;
    for(int i = 1;i <= n;i++) p[i] = i;
    for(int i = 1;i <= n;i++) cin >> a[i];
    for(int i = 1;i <= n;i++) cin >> b[i];
    LL ans = 1;
    for(int i = 1;i <= n;i++)
    {
        int x;
        cin >> x;
        if(!x && a[i] != b[i])//如果x是0 并且 俩个数字不相同
        {
            int pa = find(a[i]),pb = find(b[i]);
            if(pa == pb) ans = (ans * 2LL) % mod;//如果之前俩个数已经在一个集合当中 那么ans * 2
            else p[pa] = pb;//否则将俩个数合并到一个集合
        }
    }
    cout << ans << endl;
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        solve();
    }
}

1644C. Increase Subarray Sums(前缀和 + 线性DP)

题目大意:k从0到n 所以连续子序列(长度为len) 每次加上min(k,len) * x 的最大值

思路:先求出长度从1 到 n的连续子序列和的最大值f[1~n]

再对于k从0 ~ n求max(f[len] + min(len,k) * x)(len: 1~n);

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 5050;
int presum[N];
int f[N];

void solve()
{
    int n,p;
    cin >> n >> p;
    for(int i = 1;i <= n;i++)
    {
        cin >> presum[i];
        presum[i] += presum[i-1];//前缀和
    }
    for(int len = 1;len <= n;len ++)//枚举区间长度
    {
        f[len] = -2e9;//初始化为负无穷
        for(int l = 1;l <= n - len + 1;l++)//枚举左端点
        {
            int r = l + len - 1;
            f[len] = max(f[len],presum[r] - presum[l-1]);//计算该区间对于的和
        }
    }

    for(int k = 0;k <= n;k++)
    {
        int maxv = 0;
        for(int len = 0;len <= n;len++)
        {
            maxv = max(maxv,f[len] + min(k,len) * p);
        }
        cout << maxv <<" ";
    }
    cout << endl;

    
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        solve();
    }
    return 0;
}

1634B. Fortune Telling(思维+奇偶性)

题目大意:给定初始值 x,a[i]  A从x开始 B从x+3开始 每次可以对a[i]进行加法或者异或(0<i<n)

再给定最终的结果 判断是A变化而成 还是B变化而成

思路:1.看到只能加法或者异或 -----他们在二进制下(对于奇偶性)是等价的

           2.为什么是x 和 x+3 ----- 两者奇偶性不同

           3.数据范围超LL   ------ 不能枚举加法和异或

           4.我们只要判断一下 ans 和 初始值的奇偶性是否相同 在判断一下 a[i]的所有值 是否能改变奇偶性 

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;

void solve()
{
    LL n,st,ed;
    cin >> n >> st >> ed;
    int res = 0;
    for(int i = 0;i < n;i++)
    {
        LL a;
        cin >> a;
        res ^= a;
    }
    if(res % 2 == (st ^ ed) % 2) cout << "Alice" << endl;
    //如果结果奇偶性改变(不改变)但是a[i]不改变(能改变)那么是bob得出的结果
    else cout << "Bob" << endl;
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        solve();
    }
    return 0;
}

1679C. Rooks Defenders(树状数组)

题目大意 :每次放一辆车 可以对当前行 和 当前列造成伤害 每次查询一个矩形 判断矩形里面是否全部受到伤害

思路:前缀和 但是每次修改都要重新做一遍前缀和 时间复杂度为O(n^2)会TLE

于是用树状数组 修改和查询都是O(logn) 总时间复杂度是O(nlogn)可以过

不过要注意 一个点只能加一次 需要额外开2个数组 记录加减的次数 当次数为0且需要增加时 或者当次数为1 且要减少时 才对树状数组进行修改操作

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 100010;
int row[N],col[N];
int r[N],cl[N];
int n,q;

int lowbit(int x)
{
    return x & -x;
}

void add(int x,int c,int tr[])
{
    for(int i = x;i <= n;i += lowbit(i)) tr[i] += c;
}
int ask(int x,int tr[])
{
    int res = 0;
    for(int i = x;i > 0;i -= lowbit(i))
    {
        res += tr[i];
    }
    return res;
}


int main()
{
    cin >> n >> q;
    while(q--)
    {
        int t,x1,x2,y1,y2;
        cin >> t >> x1 >> y1;
        if(t == 1)
        {
            if(r[x1]==0) add(x1,1,row),r[x1]++;
            else r[x1] ++;
            if(cl[y1]==0) add(y1,1,col),cl[y1]++;
            else cl[y1] ++;
        }
        else if(t == 2)
        {
            if(r[x1] == 1) add(x1,-1,row),r[x1] --;
            else r[x1] --;
            if(cl[y1] == 1) add(y1,-1,col),cl[y1] --;
            else cl[y1] --;
        }
        else
        {
            cin >> x2 >> y2;
            int cntrow = ask(x2,row) - ask(x1-1,row);
            int cntcol = ask(y2,col) - ask(y1-1,col);
            if(cntrow >= x2 - x1 + 1 || cntcol >= y2 - y1 + 1) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

1682C. LIS or Reverse LIS?(思维)

题目大意:一个数组可以任意排列 求 min(从左到右最长上升子序列,从右到左最长上升子序列)

思路:对于一个数 出现2次那么 从左到右 从右到左各一次 其余出现次数(不为1)都是没用的

对于一个数出现1次 可以放中间 或者是一边 

记录cnt对于次数小于等于2的 ++,结果就是cnt/2上取整

#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
map<int,int> alls;

void solve()
{
    alls.clear();
    int n;
    cin >> n;
    int ans = 0;
    for(int i = 0;i < n;i++)
    {
        int x;
        cin >> x;
        if(!alls.count(x)) alls[x] = 1,ans ++;
        else if(alls.count(x))
        {
            alls[x]++;
            if(alls[x] <= 2) ans ++;
        }
    }
    cout << (ans+1) / 2 << endl;

}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        solve();
    }
    return 0;
}

580B. Kefa and Company(前缀和+ 结构体排序+二分)

题目大意:kefa有很多财富值不同的好友 每个好友的好感度不同 但是又不能叫他们都来 因为如果财富值相差m有好友就要伤心 求能过达到的总的好感度的最大值(不让所有人伤心的前提)

思路:对于好友的财富值排序 从左到右遍历一遍 找到能够满足财富值差值的区间 对于每一个这样的区间 求好感度 取max

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;

const int N = 100010;


struct info
{
    int w,v;
    //习惯在结构体内部重载小于号
    bool operator <(const info&t)const
    {
        return w < t.w;//按照好友财富值排序
    }
}fri[N];
int n,m;
LL s[N];

bool check(int u,int mid)
{
    if(fri[mid].w - fri[u].w < m) return true;
    return false;
}

int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        int w,v;
        cin >> w >> v;
        fri[i] = {w,v};
    }

    sort(fri+1,fri+n+1);
    for(int i = 1;i <= n;i++) s[i] = s[i-1] + (LL)fri[i].v; 
    LL ans = 0;
    for(int i = 1;i <= n;i++)//wi 要找到最右边一个 wj - wi < m的位置
    {
        int l = i,r = n;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(check(i,mid)) l = mid;//找出最右边的一个
            else r = mid - 1; 
        }
        ans = max(ans,s[r] - s[i-1]);
    }
    cout << ans << endl;
    
}

580C. Kefa and Pdfs)

题目大意:从家走到餐厅需要路过公园 kefa选择性恐猫症(不能连续遇到m只猫猫)餐厅在叶子节点,求到餐厅的个数

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 100010;
int h[N],e[N*2],w[N*2],ne[N*2],idx;
int n,m;
int ans;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

void dfs(int u,int cnt,int fa)//cnt表示连续猫猫数量
{
    if(cnt > m) return;//如果这条路连续猫猫数量超过m 那么不符合直接return
    bool isleaf = true;//判断是否走到餐厅
    for(int i = h[u]; ~i;i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;//由于建立双向边 只能往下搜 不能往上搜 不然死循环
        isleaf = false;//如果有儿子 表示不是叶子节点
        if(w[j] == 1) dfs(j,cnt+1,u);//如果该点是猫猫 那么cnt+1 
        else dfs(j,0,u);//如果不是猫猫 那么猫猫数量清0(因为要的是连续猫猫)
    }
    if(isleaf) ans++;//如果到餐厅了 答案++
}

int main()
{
    memset(h,-1,sizeof h);
    cin >> n >> m;
    for(int i = 1;i<=n;i++) cin >> w[i];//每个节点的猫猫存在情况 1表示存在

    for(int i = 0;i < n-1;i++)
    {
        int a,b;
        cin >> a >> b;
        add(a,b),add(b,a);//建立双向边
    }
    
    dfs(1,w[1],-2);//从根节点开始dfs 
    cout << ans << endl;

}

690C1. Brain Network (easy)(dfs判断环)

题目大意:一副无向图 判断图中所有点是否能到并且不存在环(也就是多余边)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 1010;
int h[N],e[N * 2],ne[N * 2],idx;
bool st[N];
int ans;
int n,m;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

bool dfs(int u,int fa)
{
    st[u] = true;
    ans ++;//统计能到达的节点数量
    for(int i = h[u];~i;i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        if(st[j]) //如果儿子之前已经找过了 那么表示形成环了
        {
            return false;//形成环直接返回false
        }
        if(!dfs(j,u)) return false;//如果儿子的儿子形成环 也返回false
    }
    return true;
}
int main()
{
    cin >> n >> m;
    memset(h,-1,sizeof h);
    for(int i = 0;i < m;i++)
    {
        int a,b;
        cin >> a >> b;
        add(a,b),add(b,a);
    }
    
    if(dfs(1,-1) && ans == n) puts("yes");//如果没有环 并且 每个点都能到达 那么满足题意
    else puts("no");
}

690C2. Brain Network (medium)(dfs+树的直径-->任意俩点距离最大值)

题目大意:找树当中任意俩点距离的最大值

思路:每个点有很多儿子 找出最长的一个儿子 和次长的儿子 相加就是距离最大值

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 100010;
int h[N],e[N * 2],ne[N * 2],idx;
int n,m;
int ans;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

int dfs(int u,int fa)//返回以u为根的最长子路
{
    //d1表示最长儿子 d2表示次长儿子
    int d1 = 0,d2 = 0;
    for(int i = h[u];~i;i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        int d = dfs(j,u) + 1;//d表示每个儿子的长度
        //更新最长与次长
        if(d > d1) d2 = d1,d1 = d;
        else if(d > d2) d2 = d;
    }
    ans = max(ans,d1 + d2);//每次更新一遍全局最大值
    return d1;
}
int main()
{
    cin >> n >> m;
    memset(h,-1,sizeof h);
    for(int i = 0;i < m;i++)
    {
        int a,b;
        cin >> a >> b;
        add(a,b),add(b,a);
    }
    dfs(1,-1);
    cout << ans << endl;
}

977E. Cyclic Components(dfs)

题目大意:求所有连通块当中 满足所有点度数为2 的联通块个数

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 200010;
bool st[N],flag;
int h[N],e[N * 2],ne[N * 2],idx;
int d[N];
int n,m;
int ans;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}


void dfs(int u)
{
    st[u] = true;
    if(d[u] != 2) flag = false;
    for(int i = h[u];~i;i = ne[i])
    {
        int j = e[i];
        if(st[j]) continue;
        dfs(j);
    }
}

int main()
{
    memset(h,-1,sizeof h);
    cin >> n >> m;
    for(int i = 0;i < m;i++)
    {
        int a,b;
        cin >> a >> b;
        d[a] ++,d[b]++;
        add(a,b),add(b,a);
    }
    for(int i = 1;i <= n;i++)
    {
        flag = true;
        if(!st[i])
        {
            dfs(i);
            if(flag) ans ++;
        }
    }
    cout << ans << endl;
}

982C. Cut 'em all!(dfs求节点数)

题目大意:在去掉尽可能多的边的条件下 满足剩下连通块点的数量相同

思路:对于点数为奇数的 显然无解 

对于点数为偶数 记录只要以u为根的节点数为偶数 (那么中间一定可以砍一刀)那么ans++

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 100010;
int h[N],e[N * 2],ne[N * 2],idx;
int n,ans;
bool st[N];

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

int dfs(int u)//返回以u为根的节点数量
{
    st[u] = true;
    int s = 1;
    for(int i = h[u];~i;i = ne[i])
    {
        int j = e[i];
        if(st[j]) continue;
        s += dfs(j);
    }
    if(s % 2 == 0) ans ++;//如果以u为根的节点数量为偶数ans++
    //从叶子节点往上算
    return s;
}

int main()
{
    memset(h,-1,sizeof h);
    cin >> n ;
    for(int i = 0;i < n - 1;i++)
    {
        int a,b;
        cin >> a >> b;
        add(a,b),add(b,a);
    }
    if(n % 2 == 1) cout << "-1";
    else
    {
        dfs(1);
        cout << ans - 1 << endl;//答案减一原因是算上了一刀不砍的偶数情况
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值