2023/7/8---个人总结

P1525 [NOIP2010 提高组] 关押罪犯

思路:贪心:把影响力最大的两个敌人放在不同监狱,用并查集维护之间的关系(在哪个监狱)。 按照影响里从大到小进行排序,遍历判断两个敌人是否在同一个监狱,如果在同一个监狱,则直接输出当前影响力 否则,将他们分配到不同的监狱(分配原则:敌人的敌人可能是朋友)。

int pre[20005];
int vis[20005];   //存放自己的敌人

struct node {
    int x,y,w;
}g[100005];

int find(int f) {
    if (pre[f] == f || pre[f] == 0)return pre[f] = f;
    else return pre[f] = find(pre[f]);
}

bool cmp(node a, node b) {
    return a.w > b.w;
}

void solve() {
    int n, m;
    cin >> n >> m;
    rep(i, 1, m) {
        cin >> g[i].x >> g[i].y >> g[i].w;
    }
    sort(g + 1, g + 1 + m, cmp);
    rep(i, 1, m) {
        int f1 = find(g[i].x);
        int f2 = find(g[i].y);
        if (f1 == f2) {         //如果在同一个监狱
            cout << g[i].w << '\n';
            return;
        }
        if (!vis[g[i].x]) {    //把自己的上一个敌人跟现在的敌人放进一个监狱
            vis[g[i].x] = g[i].y;
        } else pre[find(vis[g[i].x])] = find(g[i].y);
        if (!vis[g[i].y]) {
            vis[g[i].y] = g[i].x;
        } else pre[find(vis[g[i].y])] = find(g[i].x);
    }
    cout << 0 << '\n';
}
P1621 集合
素数筛+并查集
先把所有素数筛出来,再进行合并(用并查集维护集合,有相同的质因子则进行合并)。
const int MAXN = 162280;   //随便给的范围
vector<int> primes;    //素数表
bool isPrime[MAXN+1];
void getprimes() {    //获取素数表
    fill(isPrime, isPrime + MAXN + 1, true);
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i <= MAXN; i++) {
        if (isPrime[i]) {
            primes.push_back(i);
            for (ll j = (ll) i * i; j <= MAXN; j += i) {
                isPrime[j] = false;
            }
        }
    }
}

map<int,int>mp;
int pre[100005];
int find(int f){
    if(pre[f]==f||pre[f]==0)return pre[f]=f;
    else return pre[f]=find(pre[f]);
}

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    getprimes();
    int ans = 0;
    rep(i, n, m) {
        int x = i;
        int p = 0;
        int flag = 1;
        while (x >= primes[p]) {
            if (x % primes[p] == 0 && primes[p] >= k) {
                x /= primes[p];
                if (mp[primes[p]]) {   //合并
                    int f1 = find(primes[p]);
                    int f2 = find(i);
                    if (f1 != f2) {
                        pre[f2] = f1;
                    }
                } else {
                    mp[primes[p]] = 1;
                    pre[i] = primes[p];
                }
            } else p++;
        }
    }
    map<int, int> a;
    rep(i, n, m) {
        if (!a[find(i)])a[find(i)] = 1, ans++;
    }
    cout << ans << '\n';
}
P1955 [NOI2015] 程序自动分析

并查集+离散化

const int N = 1e7+10;

int pre[N];

int find(int f){
    if(pre[f]==f||pre[f]==0)return pre[f]=f;
    else return pre[f]=find(pre[f]);
}

struct node{
    int x,y,e;
}a[N];

bool cmp(node x,node y){
    return x.e>y.e;
}
vector<int>pos;

void solve() {
    int n;
    cin >> n;
    rep(i, 1, n) {
        cin >> a[i].x >> a[i].y >> a[i].e;
        pos.push_back(a[i].x);
        pos.push_back(a[i].y);
    }
    sort(pos.begin(), pos.end());
    pos.erase(unique(pos.begin(), pos.end()), pos.end());
    sort(a + 1, a + n + 1, cmp);
    int flag = 1;
    rep(i, 1, n) {
        int x = lower_bound(pos.begin(), pos.end(), a[i].x) - pos.begin() + 1;
        int y = lower_bound(pos.begin(), pos.end(), a[i].y) - pos.begin() + 1;
        x = find(x);
        y = find(y);
        if (a[i].e == 1) {
            if (x != y) {
                pre[y] = x;
            }
        } else {
            if (x == y) {     
                cout << "NO\n";
                flag = 0;
                break;
            }
        }
    }
    if (flag)
        cout << "YES\n";
    rep(i, 1, 1000000)pre[i] = 0;
    pos.clear();
}
B. Hamon Odyssey

 题目大意:给你一个数组a,让你进行分组,每一个组(l-r)的值为a[l]&a[l+1]...&a[r],要求每一个组的值的总和最小并且组的数量最大,输出组的数量。 贪心:要求值最小,我肯定想让它的值等于0,组数最大,把值等于0的分为一个组。 一直&下去,把等于0的分成一组(ans++,从下一个重新开始&),最后结果如果不等于0,而ans等于0的话就输出1。

void solve() {
    int n;
    cin >> n;
    int sum = 0;
    int ans = 0;
    rep(i, 1, n) {
        int x;
        cin >> x;
        if(!sum)sum = x;
        else sum &= x;
        //  cout<<sum<<' ';
        if(!sum)ans++;
    }
    if(sum&&!ans)cout<<1;
    else cout<<ans;
    cout << endl;
}
C. Vampiric Powers, anyone?

题目大意:给你一个数组a,你可以进行操作:选一个数i(1<=i<=m),生成 a[m+1]=a[i]^a[i+1]^...^a[m],m++。 无限次操作,求生成a[m+1]的最大值 。异或性质:x^y^y=x,所有我们只需要找到一个区间l,r使得a[l]^...^a[r]的值最大,其余的值可以通过再异或一次消掉。 遍历数组相当于枚举右端点,用ret异或起来,并标记每一次ret的值,加入左端点(最前的左端点为0),枚举所有左端点 消掉前面的值,取最大。

int a[100005];
bool vis[300];   //记录左端点

void solve() {
    int n;
    cin >> n;
    rep(i, 1, n)cin >> a[i];
    int vis_len = (1 << 8);
    int ans = 0;
    int ret = 0;
    vis[0] = true;  //左端点从0开始
    rep(i, 1, n) {    //枚举右端点
        ret ^= a[i];
        rep(j, 0, vis_len - 1) {    //相当于枚举左端点(把前面的值消掉)
            if(vis[j])
                ans = max(ans, ret ^ j);   //记录最大值
        }
        vis[ret] = true;   //标记左端点
    }
    cout << ans << endl;
    ms(vis, false);
}
C - Harmonic Mean
题目大意:给你一个n,问:是否存在n个数的倒数和为1,如果存在输出Yes和那n个数,否则输出No。
思路:因为1/k*(k+1)=1/k-1/(k+1),所以我们可以构建一个(2,6,12...n),但很快就会发现有问题,当n=6时,他已经在前面出现过了,我们可以这样做:n冲突的时候,n-1一定不会冲突,因为前面的序列是i*(i+1)的格式,不存在两个相邻的数,所以当n个数冲突的时候,就先构造n-1个数,n-1个数倒数求和=1,每个数乘以2后倒数求和=1/2,再加上1/2即为1。
#include <bits/stdc++.h>
#define SQR(x) fixed << setprecision(x)
#define ll long long
#define rep(i,p,n) for(int i = p;i <= n; i++)
#define dep(i,p,n) for(int i = n;i >= p; i--)
#define ms(x, y) memset(x, y, sizeof(x))
#define endl '\n'
#define YES "YES"
#define NO "NO"
#define Yes "Yes"
#define No "No"
using namespace std;

void solve() {
    int n;
    cin >> n;
    if (n == 1) {
        cout << Yes << endl;
        cout << 1 << endl;
        return;
    }
    if (n == 2) {
        cout << No << endl;
        return;
    }
    ll a[505] = {0};
    a[1] = 2;
    ll b[505] = {0};
    b[1] = 2;
    map<ll, int> mp;
    int f = 0;
    rep(i, 2, n) {
        if (i != n) {
            a[i] = (b[i - 1] * (b[i - 1] + 1));
            b[i] = b[i - 1] + 1;
        } else {
            if (!mp[b[i - 1]]) {
                a[i] = b[i - 1];
            } else {
                a[i] = 1;
                a[i - 1] /= b[i - 1];
                f = 1;
            }
        }
        mp[a[i]] = 1;
    }
    cout << Yes << endl;
    sort(a + 1, a + 1 + n);
    if (f) rep(i, 1, n)cout << a[i] * 2 << ' ';
    else
        rep(i, 1, n)cout << a[i] << ' ';
    cout << endl;
}
P1896 [SCOI2005] 互不侵犯

这是在看位运算的时候写的,枚举每一行的情况,用二进制表示每一行的状态 每次枚举时要与上一行做比较,判断是否符合条件(有没有国王互相攻击),进行状态转移。

ll f[10][1001][82];   //f[i][j][k]表示在第i行的排列情况j下用了k个国王的方案数,f[0][0][0]=1。
bool vis[1001];     //每一种排列是否符合规则(是否有连续的1)
int w[1001];         //每一个数二进制排列有多少个国王(有多少个1)

void solve() {
    int n, m;
    cin >> n >> m;
    rep(i, 0, (1 << n) - 1) {
        for (int j = i; j; j = ((j - 1) & j)) {    //计算i的二进制里有多少个1
            w[i]++;
        }
        vis[i] = ((i & (i << 1)) == 0);   //判断i的二进制里是否有连续的1
    }
    f[0][0][0] = 1;
    rep(i, 1, n) {           //从第一行开始
        rep(j, 0, (1 << n) - 1) {   //所有排列情况
            rep(k, 0, m) {         //用了多少个国王
                if (f[i - 1][j][k]) {     //判断上一行这个状态是否有方案
                    rep(l, 0, (1 << n) - 1) {   //枚举当前的所有排列情况,进行状态转移
                        //l的二进制有没有国王相互攻击     l与j有没有国王相互攻击
                        if (vis[l] && ((l & j) == 0) && (((l << 1) & j) == 0) && (((l >> 1) & j) == 0))
                            f[i][l][k + w[l]] += f[i - 1][j][k];
                    }
                }
            }
        }
    }
    ll ret = 0;
    rep(i, 0, (1 << n) - 1) {   //累加答案
        ret += f[n][i][m];
    }
    cout << ret << endl;
}

感觉这几天学习效率有点低,学习也有点拖沓,需要调整一下状态。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值