Logo handezheng 的博客

博客

标签
暂无

题解:CF1733E Conveyor

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-10-23 14:41:50

CF1733E Conveyor

题意

给定一个 $n,m$ 的矩阵,每时刻在 $(1,1)$ 上新增一个人,每个点有初始为向右的方向,每个人每时刻都会向当前点的方向走一格,然后翻转原点的方向(向右或向下)。$Q$ 次询问,每次问在 $t$ 时刻的时候,$(x,y)$ 上有没有人。

$n,m=120,x,y<120,t\le10^{18},Q\le 10^4$。

思路

看到 $t$ 很大,显然我们不能直接模拟矩阵变化的过程。

我们发现,对于一个点 $(i,j)$,它的方向一定是“右下右下右下...”的形式(第一个方向是右)。这其实就是告诉了我们,如果有 $p$ 个人经过了这个点,那么一定会有 $\lceil \frac{p}{2} \rceil$ 个人往右走,$\lfloor \frac p 2 \rfloor$ 个人往下走。

知道了这个性质,我们就可以发现,对于一个点,我们只要知道了它上面和左面走过的人数,就可以求出经过此点的人数。这个可以用 DP 求解。

那如何知道具体某个时刻一点上是否有人呢?对于询问的时刻 $t$,我们可以求出它在 $t$ 时刻走过多少人和在 $t-1$ 时刻走过多少人,如果两结果不等,那么就说明在 $t$ 时刻有人,反之没人。

单次询问复杂度 $O(nm)$,由于 $n,m$ 同阶,则总复杂度 $O(Qn^2)$。

代码

注意 DP 状态的初值。

#include<bits\/stdc++.h>
#define int long long
#define F(i,l,r) for(int i=(l); i<=(r); i++)
using namespace std;

int T;
int f[2][130][130];

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	
	cin>>T;
	F(cas,1,T){
		int t,x,y; cin>>t>>x>>y;
		memset(f,0,sizeof f);
		f[0][1][1]=t-x-y+1, f[1][1][1]=t-x-y;
		++x, ++y;
		F(i,1,x) F(j,1,y)
			F(o,0,1) f[o][i][j] += f[o][i-1][j]\/2 + (f[o][i][j-1]+1)\/2;
		cout<<(f[0][x][y]-f[1][x][y]>0?"YES\n":"NO\n");
	}
	
	return 0;
}

CSP2025游记

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-11-03 11:14:42

在一切开始前,我想说,竞赛是这样的,但我不后悔。

Day 0

到了日照之后,第一时间熟悉酒店,然后就去找 xya 和 dtw 颓去了。颓了一下午去吃饭,感觉希尔顿的伙食没有一中自选好。

然后就去试机。不出意外的又双叒叕碰到了去年CSP-S和NOIP和省选都坐我旁边的哥们 hdt ,今年他还坐我旁边。也是聊了一会。键盘还算好使,就是我那个‘N’键被扣的只剩一个‘`’了,真服了。试机的时候依旧写了线段树,今年额外写了对拍但是它祭了,准备明天找 dtw 严肃学习。

晚上是去的眼哥房间颓。玩MC玩到爽了,但是王者单排连跪。四个队友举报成功两个是啥因啊我真服了!偏偏那局还跟眼哥玩大冒险,结果眼哥玩脱了,我真的炸缸了(不过还好,拍到了眼哥私房照)。晚上吃完夜宵回房间洗了个澡,结果洗完没找着吹风机,头发还湿着就睡了/ll。

Day 1

7点准时被闹钟吵起来了。赖了会床去吃早饭,结果意外发现酒店早餐十分精致,爽吃到撑。吃完会房间睡了个回笼觉,然后找 dtw 交我写对拍,又复习了一下DP,然后颓了一会就去吃午饭了(感觉午饭也一般)。

其实这个时候已经挺紧张了,饭有点吃不下去但还是逼着自己吃饱。吃完饭收拾好东西又去找眼哥了。交流了一下,看了眼大纲,复习了平衡树和KMP(这俩掌握的不熟),然后抓紧眯了半个小时(因为已经睡不着了)。

在车上的时候依旧拿着手机颓,紧张归紧张,颓废还是不能落下的。在车上就拼命的祈祷不要考字符串

进考场就非常紧张了。尤其是考前坐在电脑前什么都不让干,然后旁边的仁兄们都胜券在握的时候。

T1

开场先看的T1,发现三方的DP非常好写,但是数据范围是1e5,于是先去看了眼T2,发现部分分非常简单,但一看T3觉得完蛋了,因为是字符串。调整了一下心态又回到T1。

发现T1有个性质,如果先按最优的放,开始大于n/2的盒子里的球放到其他盒子之后,这个盒子就只有n/2个球。因为n是偶数,所以其他盒子一定都是满足条件的。然后就发现可以贪心,把球移到第二优的盒子,然后按照减掉的贡献排序就是对的了。

100pts,想想觉得难度应该是黄,30min,信心大增,吃个士力架。

T2

先又想了想T3,看了眼T4,没思路后又转回T2了。

注意到 $k\le10$,开始的时候想着先跑一遍MST,然后挨个加入村庄再跑,每次把跑出来的边记录下次再跑,同时打标记看用了哪些村庄,最后去min。

写完大样例挂了,发现做法假了,这个东西有后效性!有可能后面的点会把前面的点挤到贡献为负。于是又想排列,发现只能过 $k\le5$ 的数据。于是想到随机化200次换顺序,但是发现如果卡的话他会把顺序卡成排列,错误率极高。于是便卡住了。

吃了个士力架,15min后想到可以状压枚举选哪些村庄,感觉像打通了任督二脉一样一切都通了。但是在计算复杂度时发现理论复杂度到了惊人的6e8!当时没多想,只是觉得虽然实现只有一秒但是卡卡常能过。写完后测大样例过了但是跑了0.5秒,于是有点慌了,想了15min还是没有优化方法,加之心态有点崩,遂放弃。有点难绷,去年70pts了今年才80pts,心态炸了。

80pts,感觉是绿,已经过了2h15min了,心态开始崩。

T4

发现T3性质A是AC自动机板子,懵了,想着考纲里没有来着,然后暴力可哈希,性质B可set,但有点难写,遂先开T4。

略想T4 $n\le18$ 10min,未想出便写全排列的做法。

8pts,已经过了2h35min,心态挺崩的。

T3

哈希可过25pts,又有set可过5pts。

这是这场比赛唯一的失误:写前还记着要判字符串长度是否相等,但是写哈希的时候前前后后挂了6次,而后抓紧写set,发现就只剩20分钟了,便急急忙忙去检查格式,检查代码是否有错等等,便把判长度抛之脑后了,警戒!!!

0~30pts,感觉是蓝紫,心态完全炸了。

离场

忘带水杯了,难绷。

问了问同学,发现今年难度确实比去年难很多。

更难绷的是,T2TMD可以用归并排序省个log!!!我真服了,这回100pts->80pts。

预估总分188pts~218pts。

后记

出分后查到了是100+80+30+8=218pts,反向挂了30分。

我又想到我去年CSP-S估分100+40+20结果也是反向挂30得100+70+20,幸得一等,看来幸运还是偏爱我的,但愿有七级勾。

听说T3 L1*L2 做法放过了40分,我看向自己的暴力拼特殊性质瞬间又不香了,算了让这个遗憾留下吧。

风物长宜放眼量,心胸要开阔。

最短路问题——Djikstra

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-16 15:51:09

最短路问题—— Dijkstra

虽然并不是完全掌握


例题:

  1. P3371 【模板】单源最短路径(弱化版)
  2. P4779 【模板】单源最短路径(标准版)
  3. Dijkstra?
  4. P5905 【模板】全源最短路(Johnson)
  5. P1144 最短路计数

Dijkstra 的条件:

  1. 用于求最短路问题
  2. 确定起点

思想

运用贪心的思想,在当前起点的出边中找到 未被遍历 过的边权最小的点,将其标记并更新最短路径,直到遍历到终点。


实现:

  1. 邻接矩阵
    时间复杂度 $O(n^2)$。
\/\/map是邻接矩阵数组,map[i][i] = 0,若i,j之间没有边则map[i][j] = INF 
void Djikstra(int x){\/\/x是起点 
	memset(d,INF,siziof(d));\/\/求最短,初始化为较大数
	d[x] = 0;\/\/到他本身
	for(int i = 1;i <= n;i++){
		int j = 0;\/\/当前最小 
		for(int k = 1;k <= n;k++){\/\/找最小的未被访问的d[j] 
			if(!vis[k] && d[k] <= d[j]){
				j=k;
			}
		}
		vis[j] = 1;
		for(int k = 1;k <= n;k++){\/\/更新d[j] 
			d[k] = min(d[k],d[j] + map[j][k]);
		}
	} 
}
  1. 邻接表

时间复杂度 $O(n^2)$。

\/\/head是表头,ver是当前边的指向的点,nxt是下一条边,data是权值 
void Djikstra(int x){\/\/x是起点 
	memset(d,INF,siziof(d));\/\/求最短,初始化为较大数
	d[x] = 0;\/\/到他本身
	for(int i = 1;i <= n;i++){
		int j = 0;\/\/当前最小 
		for(int k = 1;k <= n;k++){\/\/找最小的未被访问的d[j] 
			if(!vis[k] && d[k] <= d[j]){
				j=k;
			}
		}
		vis[j] = 1;
		for(int k = head[j];k != -1;k = nxt[k]){\/\/更新d[j] 
			d[ver[k]] = min(d[ver[k],d[j]]+data[k]);
		}
	} 
}

堆(优先队列)优化

优化条件

  1. 边权非负
  2. 边数远小于$n^2$,即$cnt(edge) \le 2\cdot10^5$

vector做法

时间复杂度 $O(m\cdot log_2m)$

struct node{
	int d;\/\/d是当前的最短路径 
	int u;\/\/u是起点 
	bool operator < (const node & rhs) const {
		return d > rhs.d;
	}\/\/重载运算符
};

vector <int> e[N];\/\/e[i][]表示第i个点所连的所有边
vector <int> w[N];\/\/w[i][]表示e[i][]所连边的权值

void Dijkstra(){
	
	priority_queue <node> q;
	F(2,i,n) d[i]=INF;
	node tn;
	tn.d=0,tn.u=1;
	q.push(tn);
	int u;
		\/\/初始化 
	
	while(!q.empty()){
		node t=q.top();
		q.pop();
		u=t.u;
		if(vis[u]) continue;\/\/要求未被标记
		vis[u]=1;
		for(int i = 0;i < e[u].size();i++){\/\/vector默认从0开始,遍历从u到达的边		 
			int v = e[u][i];\/\/v是终点
			if(d[v] > d[u] + w[u][i]){\/\/这条路径的长度小于当前最短路径 
				d[v] = d[u] + w[u][i];\/\/更新最短路径
				pre[v]=u;\/\/记忆路径
				tn.d=d[v],tn.u=v;
				q.push(tn);\/\/作为新起点入堆
			}
		} 
	}
	 
}

萌新第一次写博客

各种模板

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-19 09:18:43

板子大集

快读 和 快写

inline void read(int &x){\/\/快读
	int f = 1;
	x = 0;
	char s = getchar();
	while(s < '0' || s > '9') {
		if(s == '-') f = -1;
		s = getchar();
	}
	while(s >= '0' && s <= '9') {
		x = x * 10 + s - '0';
		s = getchar();
	}
	x *= f;
}

inline void write(int x){\/\/快写
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9) write(x \/ 10);
	putchar(x % 10 + '0');
	return ;
}

基础算法


高精度(感谢丁神的高精板子)

#include <bits\/stdc++.h>
using namespace std;
namespace CommonlyIO {
typedef long long ll;
typedef pair<int, int> PII;
#define x first
#define y second
#define sz(a) a.size()
#define lowbit(x) (x & -x)
#define debug(x) cout << #x << ':' << x << endl
#define VI vector<int>
#define all(a) a.begin(), a.end()
#define rep(i, c, n) for (int i = c; i <= n; ++i)
#define per(i, c, n) for (int i = c; i >= n; --i)
#define w(x) while (x--)
#define db double
#define pb(x) push_back(x)
#define gc() getchar()
}  \/\/ namespace CommonlyIO
using namespace CommonlyIO;
struct uinT : public vector<int> {
    const static int mod = (int)1e7;
    const static int wei = 7;
    uinT() {}
    uinT(int t) : vector<int>(1, t) {}
    uinT(vector<char> s) {
        for (int i = s.size() - 1; i >= 0; i -= wei) {
            int t = 0;
            for (int j = max(0, i - wei + 1); j <= i; ++j) {
                t = (t * 10 + (s[j] - '0'));
            }
            push_back(t);
        }
    }
    int const& operator[](const int n) const {
        return (n < size()) ? *(cbegin() + n) : (int const&)0;
    }
    int& operator[](const int n) { return *(begin() + n); }
    friend void input(uinT& a) {
        vector<char> s;
        char c = gc();
        while (!isdigit(c))
            c = gc();
        while (isdigit(c))
            s.push_back(c), c = gc();
        a = uinT(s);
    }
    friend void output(const uinT& a) {
        printf("%d", a.back());
        for (int i = a.size() - 2; i >= 0; --i) {
            printf("%0*d", wei, a[i]);
        }
    }
    friend bool operator==(uinT const& a, uinT const& b) {
        if (a.size() != b.size())
            return 0;
        for (int i = 0; i < a.size(); ++i)
            if (a[i] != b[i])
                return 0;
        return 1;
    }
    friend bool operator!=(uinT const& a, uinT const& b) { return !(a == b); }
    friend bool operator<(uinT const& a, uinT const& b) {
        if (a.size() != b.size())
            return a.size() < b.size();
        for (int i = a.size() - 1; i >= 0; --i) {
            if (a[i] != b[i])
                return a[i] < b[i];
        }
        return 0;
    }
    friend bool operator>(uinT const& a, uinT const& b) { return b < a; }
    friend bool operator<=(uinT const& a, uinT const& b) {
        if (a.size() != b.size())
            return a.size() < b.size();
        for (int i = a.size() - 1; i >= 0; --i) {
            if (a[i] != b[i])
                return a[i] < b[i];
        }
        return 1;
    }
    friend bool operator>=(uinT const& a, uinT const& b) { return b <= a; }
    friend uinT operator+(uinT const& a, uinT const& b) {
        uinT c = a;
        c.resize(max(a.size(), b.size()) + 1);
        for (int i = 0; i < b.size(); ++i) {
            c[i] += b[i];
            if (c[i] >= mod) {
                c[i] -= mod;
                c[i + 1] += 1;
            }
        }
        for (int i = b.size(); i < c.size() - 1; ++i) {
            if (c[i] >= mod) {
                c[i] -= mod;
                c[i + 1] += 1;
            }
        }
        if (c.back() == 0)
            c.pop_back();
        return c;
    }
    friend uinT operator-(uinT const& a, uinT const& b) {
        uinT c = a;
        for (int i = 0; i < b.size(); ++i) {
            c[i] -= b[i];
            if (c[i] < 0) {
                c[i] += mod;
                c[i + 1] -= 1;
            }
        }
        for (int i = b.size(); i < c.size(); ++i) {
            if (c[i] < 0) {
                c[i] += mod;
                c[i + 1] -= 1;
            } else
                break;
        }
        while (c.size() > 1 && c.back() == 0)
            c.pop_back();
        return c;
    }
    friend uinT operator*(uinT const& a, uinT const& b) {
        if (a == 0 || b == 0)
            return 0;  \/\/!
        vector<ll> t(a.size() + b.size());
        for (int i = 0; i < a.size(); ++i) {
            for (int j = 0; j < b.size(); ++j) {
                t[i + j] += 1ll * a[i] * b[j];
            }
        }
        for (int i = 0; i < t.size() - 1; ++i) {
            t[i + 1] += t[i] \/ mod;
            t[i] %= mod;
        }
        if (t.back() == 0)
            t.pop_back();
        uinT c;
        c.resize(t.size());
        for (int i = 0; i < t.size(); ++i) {
            c[i] = (int)t[i];
        }
        return c;
    }
    friend uinT operator\/(uinT const& a, uinT const& b) {
        if (a.size() < b.size())
            return 0;
        uinT c, d;
        c.assign(a.end() - b.size() + 1, a.end());
        for (int i = a.size() - b.size(); i >= 0; --i) {
            c.insert(c.begin(), a[i]);
            ll t = (c.size() > b.size())
                       ? (1ll * c.back() * mod + *(c.crbegin() + 1))
                       : (c.back());
            int l = (t \/ (b.back() + 1));
            int r = ((t + 1) \/ b.back());
            while (l < r) {
                int mid = (l + r + 1) >> 1;
                if (b * mid <= c)
                    l = mid;
                else
                    r = mid - 1;
            }
            c -= b * l;
            if (c.back() == 0)
                c.pop_back();
            d.push_back(l);
        }
        reverse(d.begin(), d.end());
        if (d.size() > 1 && d.back() == 0)
            d.pop_back();
        return d;
    }
    friend uinT operator%(uinT const& a, uinT const& b) {
        return a - a \/ b * b;
    }
    friend uinT const& operator+=(uinT& a, uinT const& b) { return a = a + b; }
    friend uinT const& operator-=(uinT& a, uinT const& b) { return a = a - b; }
    friend uinT const& operator*=(uinT& a, uinT const& b) { return a = a * b; }
    friend uinT const& operator\/=(uinT& a, uinT const& b) { return a = a \/ b; }
    friend uinT const& operator%=(uinT& a, uinT const& b) { return a = a % b; }
};

struct bigint {
    bool f;
    uinT t;
    bigint() : f(0) {}
    bigint(int t) : f(t < 0), t(uinT(abs(t))) {}
    bigint(bool f, uinT t) : f(f), t(t) {}
    friend void input(bigint& a) {
        a.f = 0;
        vector<char> s;
        char c = gc();
        for (; !isdigit(c); c = gc())
            if (c == '-')
                a.f = 1;
        while (isdigit(c))
            s.push_back(c), c = gc();
        a.t = uinT(s);
    }
    friend void output(const bigint& a) {
        if (a.f)
            putchar('-');
        output(a.t);
    }
    friend bigint abs(bigint const& a) { return bigint(0, a.t); }
    friend bool operator==(bigint const& a, bigint const& b) {
        return (a.f == b.f) && (a.t == b.t);
    }
    friend bool operator!=(bigint const& a, bigint const& b) {
        return !(a == b);
    }
    friend bool operator<(bigint const& a, bigint const& b) {
        if (a.f != b.f)
            return a.f;
        return a.f ? a.t > b.t : a.t < b.t;
    }
    friend bool operator>(bigint const& a, bigint const& b) { return b < a; }
    friend bool operator<=(bigint const& a, bigint const& b) {
        if (a.f != b.f)
            return a.f;
        return a.f ? a.t >= b.t : a.t <= b.t;
    }
    friend bool operator>=(bigint const& a, bigint const& b) { return b <= a; }
    friend bigint operator-(bigint const& a) { return bigint(!a.f, a.t); }
    friend bigint operator+(bigint const& a, bigint const& b) {
        if (a.f == b.f)
            return bigint(a.f, a.t + b.t);
        else if (a.t > b.t)
            return bigint(a.f, a.t - b.t);
        else if (a.t < b.t)
            return bigint(b.f, b.t - a.t);
        else
            return 0;
    }
    friend bigint operator-(bigint const& a, bigint const& b) {
        if (a.f == b.f) {
            if (a.t > b.t)
                return bigint(a.f, a.t - b.t);
            else if (a.t < b.t)
                return bigint(!a.f, b.t - a.t);
            else
                return 0;
        } else
            return bigint(a.f, a.t + b.t);
    }
    friend bigint operator*(bigint const& a, bigint const& b) {
        if (a == 0 || b == 0)
            return 0;
        return bigint(a.f ^ b.f, a.t * b.t);
    }
    friend bigint operator\/(bigint const& a, bigint const& b) {
        return bigint(a.f ^ b.f, a.t \/ b.t);
    }
    friend bigint operator%(bigint const& a, bigint const& b) {
        return bigint(a.f, a.t % b.t);
    }
    friend bigint const& operator+=(bigint& a, bigint const& b) {
        return a = a + b;
    }
    friend bigint const& operator-=(bigint& a, bigint const& b) {
        return a = a - b;
    }
    friend bigint const& operator*=(bigint& a, bigint const& b) {
        return a = a * b;
    }
    friend bigint const& operator\/=(bigint& a, bigint const& b) {
        return a = a \/ b;
    }
    friend bigint const& operator%=(bigint& a, bigint const& b) {
        return a = a % b;
    }
};
const int INF = 0x3f3f3f3f;
const ll INFll = 9223372036854775807;
const double PI = 3.1415926;
#define getchar()                                                          \
    (tt == ss && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) \
         ? EOF                                                             \
         : *ss++)
char In[1 << 20], *ss = In, *tt = In;
namespace Fastio {
struct Reader {
    template <typename T>
    Reader& operator>>(T& x) {
        x = 0;
        short f = 1;
        char c = getchar();
        while (c < '0' || c > '9') {
            if (c == '-')
                f *= -1;
            c = getchar();
        }
        while (c >= '0' && c <= '9')
            x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        x *= f;
        return *this;
    }
    Reader& operator>>(double& x) {
        x = 0;
        double t = 0;
        short f = 1, s = 0;
        char c = getchar();
        while ((c < '0' || c > '9') && c != '.') {
            if (c == '-')
                f *= -1;
            c = getchar();
        }
        while (c >= '0' && c <= '9' && c != '.')
            x = x * 10 + (c ^ 48), c = getchar();
        if (c == '.')
            c = getchar();
        else {
            x *= f;
            return *this;
        }
        while (c >= '0' && c <= '9')
            t = t * 10 + (c ^ 48), s++, c = getchar();
        while (s--)
            t \/= 10.0;
        x = (x + t) * f;
        return *this;
    }
    Reader& operator>>(long double& x) {
        x = 0;
        long double t = 0;
        short f = 1, s = 0;
        char c = getchar();
        while ((c < '0' || c > '9') && c != '.') {
            if (c == '-')
                f *= -1;
            c = getchar();
        }
        while (c >= '0' && c <= '9' && c != '.')
            x = x * 10 + (c ^ 48), c = getchar();
        if (c == '.')
            c = getchar();
        else {
            x *= f;
            return *this;
        }
        while (c >= '0' && c <= '9')
            t = t * 10 + (c ^ 48), s++, c = getchar();
        while (s--)
            t \/= 10.0;
        x = (x + t) * f;
        return *this;
    }
    Reader& operator>>(char& c) {
        c = getchar();
        while (c == ' ' || c == '\n' || c == '\r')
            c = getchar();
        return *this;
    }
    Reader& operator>>(char* str) {
        int len = 0;
        char c = getchar();
        while (c == ' ' || c == '\n' || c == '\r')
            c = getchar();
        while (c != ' ' && c != '\n' && c != '\r')
            str[len++] = c, c = getchar();
        str[len] = '\';
        return *this;
    }
    Reader& operator>>(string& str) {
        str.clear();
        char c = getchar();
        while (c == ' ' || c == '\n' || c == '\r')
            c = getchar();
        while (c != ' ' && c != '\n' && c != '\r')
            str.push_back(c), c = getchar();
        return *this;
    }
    Reader& operator>>(bigint& a) {
        input(a);
        return *this;
    }
    Reader& operator>>(__float128& x) {
        x = 0;
        __float128 t = 0;
        short f = 1, s = 0;
        char c = getchar();
        while ((c < '0' || c > '9') && c != '.') {
            if (c == '-')
                f *= -1;
            c = getchar();
        }
        while (c >= '0' && c <= '9' && c != '.')
            x = x * 10 + (c ^ 48), c = getchar();
        if (c == '.')
            c = getchar();
        else {
            x *= f;
            return *this;
        }
        while (c >= '0' && c <= '9')
            t = t * 10 + (c ^ 48), s++, c = getchar();
        while (s--)
            t \/= 10.0;
        x = (x + t) * f;
        return *this;
    }
    Reader() {}
} cin;
const char endl = '\n';
struct Writer {
    const int Setprecision = 6;
    typedef int mxdouble;
    template <typename T>
    Writer& operator<<(T x) {
        if (x == 0) {
            putchar('0');
            return *this;
        }
        if (x < 0)
            putchar('-'), x = -x;
        static short sta[40];
        short top = 0;
        while (x > 0)
            sta[++top] = x % 10, x \/= 10;
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        return *this;
    }
    Writer& operator<<(const bigint& n) {
        output(n);
        return *this;
    }
    Writer& operator<<(__float128 x) {
        if (x < 0)
            putchar('-'), x = -x;
        mxdouble _ = x;
        x -= (__float128)_;
        static short sta[40];
        short top = 0;
        while (_ > 0)
            sta[++top] = _ % 10, _ \/= 10;
        if (top == 0)
            putchar('0');
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        putchar('.');
        for (int i = 0; i < Setprecision; i++)
            x *= 10;
        _ = x;
        while (_ > 0)
            sta[++top] = _ % 10, _ \/= 10;
        for (int i = 0; i < Setprecision - top; i++)
            putchar('0');
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        return *this;
    }
    Writer& operator<<(double x) {
        if (x < 0)
            putchar('-'), x = -x;
        mxdouble _ = x;
        x -= (double)_;
        static short sta[40];
        short top = 0;
        while (_ > 0)
            sta[++top] = _ % 10, _ \/= 10;
        if (top == 0)
            putchar('0');
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        putchar('.');
        for (int i = 0; i < Setprecision; i++)
            x *= 10;
        _ = x;
        while (_ > 0)
            sta[++top] = _ % 10, _ \/= 10;
        for (int i = 0; i < Setprecision - top; i++)
            putchar('0');
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        return *this;
    }
    Writer& operator<<(long double x) {
        if (x < 0)
            putchar('-'), x = -x;
        mxdouble _ = x;
        x -= (long double)_;
        static short sta[40];
        short top = 0;
        while (_ > 0)
            sta[++top] = _ % 10, _ \/= 10;
        if (top == 0)
            putchar('0');
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        putchar('.');
        for (int i = 0; i < Setprecision; i++)
            x *= 10;
        _ = x;
        while (_ > 0)
            sta[++top] = _ % 10, _ \/= 10;
        for (int i = 0; i < Setprecision - top; i++)
            putchar('0');
        while (top > 0)
            putchar(sta[top] + '0'), top--;
        return *this;
    }
    Writer& operator<<(char c) {
        putchar(c);
        return *this;
    }
    Writer& operator<<(char* str) {
        int cur = 0;
        while (str[cur])
            putchar(str[cur++]);
        return *this;
    }
    Writer& operator<<(const char* str) {
        int cur = 0;
        while (str[cur])
            putchar(str[cur++]);
        return *this;
    }
    Writer& operator<<(string str) {
        int st = 0, ed = str.size();
        while (st < ed)
            putchar(str[st++]);
        return *this;
    }
    Writer() {}
} cout;
}  \/\/ namespace Fastio
using namespace Fastio;
#define cin Fastio::cin
#define cout Fastio::cout
#define endl Fastio::endl
#define __int128 bigint

矩阵(取模)

int mod = ;\/\/模数自己设
struct Matrix{
	int n,m;
	int a[205][205];
	
	inline void init(){ memset(a,0,sizeof a); }
	inline void init_I(){
		init();
		F(i,1,min(n,m)) a[i][i]=1;
	}

    inline friend void operator = (Matrix &a,Matrix b){
		a.n=b.n,a.m=b.m;
		F(i,1,a.n) F(j,1,a.m) a.a[i][j] = b.a[i][j];
	}
	inline friend Matrix operator + (Matrix a,Matrix b){
		Matrix c; c.init();
		c.n=a.n, c.m=a.m;
		F(i,1,c.n) F(j,1,c.m) c.a[i][j] = (a.a[i][j] + b.a[i][j]) % mod;
		return c;
	}
	inline friend Matrix operator - (Matrix a,Matrix b){
		Matrix c; c.init();
		c.n=a.n, c.m=a.m;
		F(i,1,c.n) F(j,1,c.m) c.a[i][j] = (a.a[i][j] - b.a[i][j]) % mod;
		return c;
	}
	inline friend Matrix operator * (Matrix a,Matrix b){
		Matrix c; c.init();
		c.n = a.n, c.m = b.m;
		F(i,1,c.n) F(j,1,c.m) F(k,1,a.m)
			c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % mod;
		return c;
	}
	inline friend bool operator == (Matrix a,Matrix b){
		if(a.n!=b.n || a.m!=b.m) return 0;
		F(i,1,a.n) F(j,1,a.m) if(a.a[i][j] != b.a[i][j]) return 0;
		return 1;
	}
	inline friend bool operator != (Matrix a,Matrix b){ return !(a==b); }
	inline friend void operator += (Matrix &a,Matrix b){ a=a+b; }
	inline friend void operator -= (Matrix &a,Matrix b){ a=a-b; }
};

线性筛(带了欧拉函数和莫比乌斯函数)

int prm[N], tot;
int phi[N], mu[N];
inline void ola(){
	F(i,2,N-50){
		if(!p[i]){
			prm[++tot] = i;
			phi[i] = i-1;
			mu[i] = -1;
		}
		for(int j=1; j<=tot && i*prm[j]<=N-50; j++){
			p[i] = true;
			if(i%prm[j]){
				phi[i*prm[j]] = phi[i] * phi[prm[j]];
				mu[i*prm[j]] = -mu[i];
			}else{
				phi[i*prm[j]] = phi[i] * prm[j];
				mu[i*prm[j]] = 0;
				break;
			}
		}
	}
}

快速幂

inline int fast_power(int x,int p){
  \/\/求x的p次方
	int ans = 1;
	while(p){
		if(p % 2) ans = ans * x % mod;
		x = x * x % mod;
		p \/= 2;
	}
	return ans;
}

建图——邻接表的连边

无边权

int tot,head[N],ver[N],nxt[N];

inline void add(int x,int y){
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}

有边权

int tot,head[N],ver[N],nxt[N],d[N];

inline void add(int x,int y,int z){
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
	d[tot] = z;
}

图论

DSU 并查集

int dsu[N];

inline int find(int x){\/\/查找根节点 
	return dsu[x] == x ? x : dsu[x] = find(dsu[x])
}

inline void merge(int x,int y){\/\/合并两棵树 
	int xx = find(x), yy = find(y);
	dsu[xx] = yy;
}

inline bool union_find(int x,int y){\/\/查找两个节点是否在同一棵树上,即根节点是否相同 
	return find(x) == find(y);
}

MST 最小生成树

Kruskal

int n,m;
int dsu[M];
struct edge{
	int u,v,val;
}a[N];
inline int find(int x){
	return (x == dsu[x]) ? x : dsu[x] = find(dsu[x]);
}
inline void merge(int x,int y){
	int xx = find(x),yy = find(y);
	dsu[xx] = yy;
}
inline bool cmp(edge a,edge b){
	return a.val < b.val;
}
signed main(){
	sort(a + 1,a + m + 1,cmp);
	int cnt = 0,ans = 0;
	F(1,i,m){
		if(find(a[i].u) != find(a[i].v)){
			cnt ++;
			ans += a[i].val;
			merge(a[i].u,a[i].v);
		}
		if(cnt == n - 1) break;
	}
	cout << ans;
	return 0;
}

数论

拓展欧几里得

inline int exgcd(int a,int b,int &x,int &y){
	if(b == 0){
		x = 1,y = 0;
		return a;
	}
	int x2,y2;
	int d = exgcd(b,a % b,x2,y2);
	x = y2,y = x2 - a \/ b * y2;
  return d; 
}

字符串算法

Trie 字典树

int idx,tr[N][70],cnt[N];
char c[N];

inline int gt(char c){
	if(c>='A'&&c<='Z') return c-'A';
	if(c>='a'&&c<='z') return c-'a'+26;
	else return c-'0'+52;
}
inline void insert(char c[]){
	int len=strlen(c),p=0;
	F(i,0,len-1){
		int t=gt(c[i]);
		if(!tr[p][t]) tr[p][t]=++idx;
		p=tr[p][t];
		cnt[p]++;
	}
}
inline int find(char c[]){
	int len=strlen(c),p=0;
	F(i,0,len-1){
		int t=gt(c[i]);
		if(!tr[p][t]) return 0;
		p=tr[p][t];
	}
	return cnt[p];
}

最小表示法

inline string get_min_expression(string s){
	int k=0,i=0,j=1,n=s.size();
	while(i<n && j<n && k<n){
		if(s[(i+k)%n] == s[(j+k)%n]) k++;
		else{
			s[(i+k)%n]>s[(j+k)%n] ? i+=k+1 : j+=k+1;
			if(i==j) i++;
			k=0;
		}
	}
	i=min(i,j);
	return s.substr(i)+s.substr(0,i);
}

树形数据结构

Treap 无旋平衡树

struct Treap{
	#define ls (tr[rt].l)
	#define rs (tr[rt].r)
	struct treap{
		int l, r;\/\/表示其左右儿子的结点编号
		int val, rnd;\/\/BST的权值与heap的值
		int siz;\/\/以其为根的子树的结点个数
	}tr[N];
	int root = 0, n = 0;
	\/\/分别代表根和数的个数
	
	inline void newnode(int v){
		tr[++n].val = v;
		tr[n].rnd = rng();
		tr[n].siz = 1;
	}\/\/新增一个结点
	
	inline void pushup(int rt){
		tr[rt].siz = tr[ls].siz + tr[rs].siz +1;
	}
	inline void split(int rt, int v, int &x, int &y){
		if(rt == 0){
			x = y = 0;
			return ;
		}
		
		if(tr[rt].val <= v){
			x = rt;
			split(rs, v, rs, y);
		}else{
			y = rt;
			split(ls, v, x, ls);
		}\/\/极其重要!极其巧妙! 
		
		pushup(rt);
	}
	inline int merge(int x, int y){
		if(!x || !y) return x+y;
		if(tr[x].rnd < tr[y].rnd){
			tr[x].r = merge(tr[x].r, y);
			pushup(x);
			return x;
		}else{
			tr[y].l = merge(x, tr[y].l);
			pushup(y);
			return y;
		}
	}\/\/基本操作:分裂与合并
	
	inline void insert(int v){
		int x, y;
		split(root, v, x, y);
		newnode(v);
		int z = n;
		root = merge(merge(x, z), y);
	}\/\/插入结点
	inline void del(int v){
		int x, y, z;
		split(root, v, x, z);
		split(x, v-1, x, y);
		y = merge(tr[y].l, tr[y].r);\/\/若y有子树,合并(保留)其子树 
		root = merge(merge(x, y), z);
	}\/\/删除结点 
	inline int rank(int v){
		int x, y;
		split(root, v-1, x, y);
		int ans = tr[x].siz + 1;
		root = merge(x, y);
		return ans;
	}\/\/查找排名 
	inline int topk(int rt, int k){
		int lsz = tr[ls].siz;
		if(k == lsz+1) return tr[rt].val;
		if(k <= lsz) return topk(ls, k);
		return topk(rs, k - lsz - 1);
	}\/\/查询第k小的数 
	inline int get_pre(int v){
		int x, y;
		split(root, v-1, x, y);
		int ans = topk(x, tr[x].siz);
		root = merge(x, y);
		return ans;
	}\/\/查找前驱 
	inline int get_suc(int v){
		int x, y;
		split(root, v, x, y);
		int ans = topk(y, 1);
		root = merge(x, y);
		return ans;
	}\/\/查找后继 
}T;

Splay 平衡树

struct Splay{
	int rt=0,tot=0;
	struct tree{
		int s[2];	\/\/s[0]表示左儿子,s[1]表示右儿子 
		int siz,val;
		int fa;
	}tr[N<<1];
	#define fa(x) tr[x].fa
	#define ls(x) tr[x].s[0]
	#define rs(x) tr[x].s[1]
	inline void pushup(int x){ tr[x].siz = tr[ls(x)].siz+tr[rs(x)].siz+1; }
	inline void clear(int x){ tr[x].siz=fa(x)=ls(x)=rs(x)=tr[x].val=0; }
	inline bool get(int x){
		return rs(fa(x))==x;
	}\/\/返回 0 表示左儿子,返回 1 表示右儿子 
	
	inline void newnode(int val){
		clear(++tot);
		tr[tot].val = val;
		tr[tot].siz=1;
	}
	inline void ronate(int x){
		int c = get(x), y = fa(x), z = fa(y);
		if(tr[x].s[!c]) fa(tr[x].s[!c]) = y;	\/\/若有与自身左右不一样的儿子,则"过继"至原父亲
		tr[y].s[c] = tr[x].s[!c];
		tr[x].s[!c] = y; fa(y) = x;		\/\/原父亲成为儿子
		fa(x) = z;			\/\/原儿子成为原先父亲的父亲的儿子 
		if(z) tr[z].s[(y==rs(z))] = x;
		pushup(y), pushup(x);
	}
	inline void splay(int x){
		while(fa(x)){
			int y = fa(x);
			if(fa(y)) ronate(get(x)==get(y) ? y : x);	\/\/若自身与父亲同向,则先旋转父亲以维护平衡性
			ronate(x);
		}
		rt=x;
	}\/\/将某个非根节点旋转至根上 
	
	inline void insert(int val){
		int now=rt, f=0;
		while(now){
			f = now;
			now = tr[now].s[val > tr[now].val];
		}\/\/在二叉搜索树上寻找当前权值的节点应在的位置 
		newnode(val);		\/\/创建新节点 
		fa(tot) = f;
		tr[f].s[val > tr[f].val] = tot;		\/\/将新创建的节点与其父亲连边 
		splay(tot);		\/\/将新节点旋转至根 
	}\/\/插入新节点 
	inline void del(int val){
		int now=rt,f=0;
		while(now && tr[now].val != val){
			f=now;
			now = tr[now].s[val>tr[now].val];
		}\/\/根据权值,在二叉搜索树上寻找应删除节点的位置 
		if(!now){
			splay(f);
			return ;
		}\/\/若没有则旋转其父亲,并直接返回 
		splay(now);
		int cur = ls(now);
		if(!cur){		\/\/若要删除的点没有左儿子 
			rt = rs(now);	\/\/右儿子为根 
			if(rt) fa(rt) = 0;
			clear(now);	\/\/删除 
			return ;
		}
		while(rs(cur)) cur = rs(cur);	\/\/找到左子树中最右边的叶子 
		rs(cur) = rs(now);
		if(rs(now)) fa(rs(now)) = cur;	\/\/右子树的根成为该叶子的儿子以维护二叉搜索树的性质 
		fa(ls(now)) = 0;
		clear(now);			\/\/删除节点 
		pushup(cur);
		splay(cur);
	}\/\/删除一个节点 
	inline int rhk(int val){
		int res=1,now=rt,f=0;
		while(now){
			f = now;
			if(tr[now].val<val){
				res += tr[ls(now)].siz+1;	\/\/左子树上的点和当前点都比其小
				now = rs(now);
			}else now = ls(now);
		}
		splay(f);
		return res;
	}\/\/查询某个数的 rank 值
	inline int kth(int cnt){
		int now=rt;
		while(now){
			int t = tr[ls(now)].siz+1;
			if(t < cnt) cnt -= t, now=rs(now);
			else if(t==cnt) break;
			else now=ls(now);
		}
		splay(now);
		return tr[now].val;
	}\/\/查询第 k 小的值 
	inline int pre(int val){
		int res=0,now=rt,f=0;
		while(now){
			f=now;
			if(tr[now].val>=val) now=ls(now);
			else res=tr[now].val,now=rs(now);
		}
		splay(f);
		return res;
	}\/\/查询前驱 
	inline int nxt(int val){
		int res=0,now=rt,f=0;
		while(now){
			f=now;
			if(tr[now].val<=val) now=rs(now);
			else res=tr[now].val,now=ls(now);
		}
		splay(f);
		return res;
	}\/\/查询后继 
}T;

警钟撅烂——并查集

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-20 16:31:44

并查集的易错点


时间复杂度

P1197 [JSOI2008] 星球大战 (题目思路见 题解提交记录

易错点:
并查集的查找根节点

inline int find(int x){
    if(dsu[x] == x) return x;
    return find(dsu[x]);
}

仔细想想好像并没有什么不合适
但是可以优化!

inline int find(int x){
    if(dsu[x] == x) return x;
    return dsu[x] = find(dsu[x]);
}

看出区别来了:
如果用上面的方法,每次查找要花的时间是这个节点的深度
但是优化了之后,一次查找要花的时间还是它的深度
每查找一次之后,将节点直接连接成根,节点的深度变为1,就会变成类似菊花图的并查集,再查找时仅需花O(1)的复杂度!

看上去没少多少,但是如果不优化,就会有两个点T!点击查看
废了我两个小时 将警钟撅烂!!

树状数组

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-03-16 10:33:48

树状数组


明确

树状数组仅能实现两个功能:

  1. 单点修改
  2. 区间查询

表示


如图,以 4 为例,二进制是100,它是010(2)和011(3)的父亲,是1000(8)的儿子
那么,对于c,它的父亲是二进制下,最后一个 1 (lowbit(c)) + 1

区间查询

令c[i]等于它管辖的数的和
以7为例,即111 求区间[1,7]就等于c[111] +c[110]+c[100]
下附模板:

int find(int u){
	int ans = 0;
	while(u){
		ans += c[u];
		u -= u&-u;\/\/去掉最后一个零
	}
	return ans;
}

求区间[l,r]即求[1,r] - [1,l - 1]

单点修改

注意:修改时应将其父亲,父亲的父亲...都修改
模板:

void add(int u,int x){\/\/将第u个数增加x
	while(u <= n){
		c[u] += x;
		u += u&-u;\/\/将最后一个零进一位
	}
}

谨记

相对于对于树状数组的两个功能,更重要的是将题意转化为树状数组能够实现的功能
树状数组2 的思路就是维护一个差分数组,将区间修改转化为两个区间端点的单点修改,将单点查询转化为查分数组的前缀和

好题

类似的树状数组题目还有 P1083 [NOIP2012 提高组] 借教室P1908 逆序对

P3372 【模板】线段树 1

特别的,对于 P3372 【模板】线段树 1 中的区间修改、区间求和问题:
我们可以维护两个树状数组,
第一个如树状数组2中的,是一个查分的树状数组,这样就实现了区间修改功能
而在查询时,现将查分树状数组进行前缀和,实现单点查询,再维护一个树状数组,用以前缀和第一个树状数组的前缀和,这样就实现了区间查询的功能

P1063 [NOIP2006 提高组] 能量项链 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-06-03 21:50:51

这道题细节坑点很多,基本思路是区间DP

题目传送门

思路

性质

区间DP的其中一种有一个特点(性质):大区间的解能由小区间的解合并而来

聚合的本质

而通过读题我们可以得到,一串珠子聚合后,本质还是一个珠子,其头标记是最前面(左端点)的头标记,其尾标记是最后面(右端点)的尾标记,只不过带了聚合的能量而已

那么,我们可以想到,一个大珠子(大区间)可以由两个较小的珠子(小区间) (与两颗珠子的大小无关,仅需保证能够聚合成大珠子即可,即断点的位置不影响聚合成这颗珠子) 聚合(合并)而来。
所以,我们可以用区间DP来解决本题,方式是枚举断点,从而寻找最优解

状态

状态定义

f[l][r]表示区间左端点为l,右端点为r时释放的最大能量

状态转移

转移条件:无
转移方程:聚合后的区间为[l,r]

设s为断点,新增珠子释放的能量:
[l,s]的头 * [s+1,r]的头([l,s]的尾) * [s+1,r]的尾([r+1, ]的头) = a[l] * a[s + 1] * a[r + 1];

再加上两个小区间本来的能量,得状态转移方程:
$$f[l][r] = \max{f[l][r],f[l][s]+f[s+1][r] + a[l]\times a[s+1]\times a[r+1]};$$

细节

  1. 题目明确给出,这是一个环,我们要通过把标记数组a 乘二的方式破环为链
  2. 题目中只给了头标记,遇到某一颗或某一串珠子的尾标记是,要将其转为下一颗珠子的头标记
  3. 循环顺序问题:
    若先遍历l再遍历r,可能出现已经转移到了[1,n]但[4,5][n-1,n]等区间尚未计算
    正确方式为:先遍历len(区间长度)再遍历l,从而求出r
  4. 答案不是f[1][n]!! 我们已经破环为链,而在实际的环中,任何一个点都有可能是链的起点,所以答案应为MAX{f[i][i+n-1]}

代码

核心代码仅供参考

	re int r;
	for(int len = 2;len <= n;len ++){\/\/枚举 区间长度 
		for(int l = 1;l < n * 2 && l + len - 1 <= n * 2;l ++){
			\/\/长度最多为n,右端点不超过2*n 
			r = l + len - 1;
			for(int s = l;s < r;s ++){\/\/枚举断点
				f[l][r] = max(f[l][r],f[l][s]+f[s+1][r] + a[l]*a[s+1]*a[r+1]);
			}
		}
	}

题解:CF1249D2 Too Many Segments (hard version)

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-06-12 22:33:19

题目传送门

前言:

这道题是 CF1249D1 Too Many Segments (easy version) 的加强版,建议读者先去做一下此题,或是看一下它的题解,理解这两道题 贪心 的基本思路,以免看得一头雾水。

思路

额,本来想略过的,楼上 dalao 讲的很清楚了,但考虑到这是我洛谷上的第一篇题解,还是讲一讲吧。
题目要求我们删除一部分线段使得没有“坏点”的存在,那我们肯定要遍历一遍区间,找到哪个点是“坏点”后,才能进行操作。

当我们枚举到一个点时,如果这个点是“坏点”,那它肯定被多条线段所覆盖,枚举到这个点时,它前面的点一定都不是“坏点”(本来就不是“坏点”或在前面已经删掉一些线段使它不是“坏点”)。
那么当前覆盖这个点的线段的左端点对其毫无用处,无论删除多少条线段都不会改变这个点左面所有点是不是“坏点”,那我们可以关注右端点

贪心地想,一条线段的右端点越“远”,那它就对更多的点有影响(贡献越大)。
所以,我们应该尽量删除右端点更靠右的线段

细节及实现

实现

  • 数据范围:$n \le 2\times10^5$ 并且 $l,r \le 2\times10^5$,这意味着遍历区间所有点就需要花费 $O(r_{max})$ 的时间复杂度,所以就无法再花费 $O(n)$ 的复杂度来枚举所有线段了。
  • 考虑优化。我们可以用一个数据结构来存储覆盖到当前点的线段,考虑到要需要用排序维护右端点更靠右,最好用 priority_ququeset(我个人推荐用 set,因为 priority_quque 仅支持 priority_queue.top() 的删除)。
  • 遍历到一个点时,可能会有多条线段以它为左端点,所以我们可以用二维的 vector 来存储线段的有关信息。

细节

  • 题目要求输出线段的编号,所以我们的 setvector 都是结构体,包括线段右端点和线段编号。
  • 在遍历到一个新的点时,我们要把右端点小于当前点的线段删掉(容易想到这样的点已经“过期”了,对后续操作无用)。
  • 我们在删除线段时仅保证右端点尽量大,不保证它的编号是升序的,所以要对 $ans$ 数组进行排序(以编号为关键字)。
  • 感谢 STL 库 set 中有一个不常用的功能:set.rbegin()set.end() 的前一个位置,在本题中就是右端点最大点所在的位置。
  • set.begin()set.rbegin() 等返回的都是元素所在的位置,使用时应加上符号“*”。

AC code

应该有部分读者最喜欢看这个,所以把它安排在最后

#include<bits\/stdc++.h>
#include<vector>
#include<set>
using namespace std;
#define int long long
#define re register
#define F(j,i,n)  for(re int i = j;i <= n;i++)
const int N = 2e5 + 50,M = 1e3 + 50;
inline void read(int &x){
	re int f = 1;x = 0;
	re char s = getchar();
	while(s < '0' || s > '9'){
		if(s == '-') f = -1;
		s = getchar();
	}while(s >= '0' && s <= '9')
		x = x * 10 + s - '0',s = getchar();
	x *= f;
}

int n,k;
int tot;\/\/被删除线段的数量 
struct node{
	int r,id;
	\/\/r记录区间的右端点
	\/\/id记录当前区间的编号
}ans[N];\/\/被删除线段的编号 
inline bool operator < (node a,node b){
	if(a.r == b.r) return a.id < b.id;
	return a.r < b.r;
	\/\/以右端点为关键字
}
inline bool cmp(node a,node b){
	return a.id < b.id;
	\/\/以编号为关键字升序排序
}
vector <node> a[N];
\/\/a[l]记录以l为左端点的线段的相关信息 
set <node> st;
\/\/set记录覆盖到当前点的线段 

signed main(){
	
	read(n),read(k);
	int l,r;
	node tmp;
	F(1,i,n){
		read(l),read(r);
		tmp.r = r,tmp.id = i;
		a[l].push_back(tmp);
	}
	
	F(1,i,200000){
		while(st.size() && (*st.begin()).r < i){\/\/区间的右端点小于当前点,说明这个区间已"过期"
			st.erase(*st.begin());
		}\/\/删除"过期"的区间
		
		for(int j = 0;j < a[i].size();j ++){
			st.insert(a[i][j]);
		}\/\/把以当前遍历到的点为左端点的区间插入至set
		
		while(st.size() > k){\/\/如果这个点是坏点
			ans[++ tot] = *st.rbegin();\/\/记录要被删除的区间
			st.erase(*st.rbegin());\/\/删除区间
		}
	}
	printf("%lld\n",tot);
	sort(ans + 1,ans + tot + 1,cmp);
	F(1,i,tot) printf("%lld ",ans[i].id);
	
	return 0;
}

最后的最后

本蒟蒻第五次申请题解,希望管理大大高抬贵手,把审核通过了吧~
第一次被打回:2024-06-13 10:08
第二次被打回:2024-07-05 14:18
第三次被打回:2024-07-08 15:47
第四次被打回:2024-07-10 22:21

题解·Too Many Segments (easy version)

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-18 11:26:57

题目传送门

前言:

前置芝士

做完的可以去做 CF1249D1 Too Many Segments (hard version),它是本题的加强版,我在本题中思路的实现和代码均是参考它的数据范围的。

思路

题目要求我们删除一部分线段使得没有“坏点”的存在,那我们肯定要遍历一遍区间,找到哪个点是“坏点”后,才能进行操作。

当我们枚举到一个点时,如果这个点是“坏点”,那它肯定被多条线段所覆盖,枚举到这个点时,它前面的点一定都不是“坏点”(本来就不是“坏点”或在前面已经删掉一些线段使它不是“坏点”)。
那么当前覆盖这个点的线段的左端点对其毫无用处,无论删除多少条线段都不会改变这个点左面所有点是不是“坏点”,那我们可以关注右端点

贪心地想,一条线段的右端点越“远”,那它就对更多的点有影响(贡献越大)。
所以,我们应该尽量删除右端点更靠右的线段

细节及实现

实现

  • 数据范围:$n \le 200$ 并且 $l,r\le 200$,我们遍历区间所有点只需要花费 $O(r_{max})$ 的时间复杂度,所以就可以再花费 $O(n)$ 的复杂度来枚举所有线段了。
    但对于加强版,有 $n \le 2\cdot10^5$ 且 $l,r\le 2\cdot10^5$,明显上述做法的时间会爆掉。

  • 考虑优化。我们可以用一个数据结构来存储覆盖到当前点的线段,考虑到要需要用排序维护右端点更靠右,最好用 priority_queueset(我个人推荐用 set,因为 priority_queue 仅支持 priority_queue.top() 的删除)。

  • 遍历到一个点时,可能会有多条线段以它为左端点,所以我们可以用二维的 vector 来存储线段的有关信息。

细节

  • 题目要求输出线段的编号,所以我们的 setvector 都是结构体,包括线段右端点和线段编号。
  • 在遍历到一个新的点时,我们要把右端点小于当前点的线段删掉(容易想到这样的点已经“过期”了,对后续操作无用)。
  • 我们在删除线段时仅保证右端点尽量大,不保证它的编号是升序的,所以要对 $ans$ 数组进行排序(以编号为关键字)。
  • 感谢 STL 库 set 中有一个不常用的功能:set.rbegin()set.end() 的前一个位置,在本题中就是右端点最大点所在的位置。
  • set.begin()set.rbegin() 等返回的都是元素所在的位置,使用时应加上符号“*”。

AC code

应该有部分读者最喜欢看这个,所以把它安排在最后

这份代码在 加强版 中也是可以 通过 的。双倍经验

#include<bits\/stdc++.h>
#include<vector>
#include<set>
using namespace std;
#define int long long
#define re register
#define F(j,i,n)  for(re int i = j;i <= n;i++)
const int N = 2e5 + 50,M = 1e3 + 50;
inline void read(int &x){
	re int f = 1;x = 0;
	re char s = getchar();
	while(s < '0' || s > '9'){
		if(s == '-') f = -1;
		s = getchar();
	}while(s >= '0' && s <= '9')
		x = x * 10 + s - '0',s = getchar();
	x *= f;
}

int n,k;
int tot;\/\/被删除线段的数量 
struct node{
	int r,id;
	\/\/r记录区间的右端点
	\/\/id记录当前区间的编号
}ans[N];\/\/被删除线段的编号 
inline bool operator < (node a,node b){
	if(a.r == b.r) return a.id < b.id;
	return a.r < b.r;
	\/\/以右端点为关键字
}
inline bool cmp(node a,node b){
	return a.id < b.id;
	\/\/以编号为关键字升序排序
}
vector <node> a[N];
\/\/a[l]记录以l为左端点的线段的相关信息 
set <node> st;
\/\/set记录覆盖到当前点的线段 

signed main(){
	
	read(n),read(k);
	int l,r;
	node tmp;
	F(1,i,n){
		read(l),read(r);
		tmp.r = r,tmp.id = i;
		a[l].push_back(tmp);
	}
	
	F(1,i,200000){
		while(st.size() && (*st.begin()).r < i){\/\/区间的右端点小于当前点,说明这个区间已"过期"
			st.erase(*st.begin());
		}\/\/删除"过期"的区间
		
		for(int j = 0;j < a[i].size();j ++){
			st.insert(a[i][j]);
		}\/\/把以当前遍历到的点为左端点的区间插入至set
		
		while(st.size() > k){\/\/如果这个点是坏点
			ans[++ tot] = *st.rbegin();\/\/记录要被删除的区间
			st.erase(*st.rbegin());\/\/删除区间
		}
	}
	printf("%lld\n",tot);
	sort(ans + 1,ans + tot + 1,cmp);
	F(1,i,tot) printf("%lld ",ans[i].id);
	
	return 0;
}

最后

如果有代码、思路问题,错别字问题及其他问题欢迎私聊

游戏集

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-18 18:46:30

关注HDZ谢谢

mc

io

dogod.io
mope.io
开采器shapez.io
神神子的hole.io
你画我猜gartic.io
deeeep.io(鱼吃鱼)
krunker大型像素枪战
吃鸡surviv.io
florr.io
小蝌蚪
贪吃蛇
kirka.io
ev.io
yorg.io(好像是丧尸、塔防)
starblast
yumy
坦克游戏

gamepix.com全是io游戏!
iogames网站


国际象棋(人机)

国际象棋(PVP&人机)

【投稿】协同放置(3000000玩家的超大型放置游戏)【失效】

【投稿】无尽的饼干(v2.048)【失效】

文明放置2

c++游戏:王权

名字竞技场!

数 学 作 图 题

hacker模拟器

物理画线

围地大作战

生火间

Chrome小恐龙

对诗词(用括号把对的字替代掉哦)

generals.io将军棋

脆皮(类似文明放置)

JC模拟器 共创世界

找牛

地下探险队

卡牌冒险者

卡牌冒险者2

塔防(还行)

超级玛丽奥

海盗大逃杀yohoho.io

digdig.io(挖掘)

在线下棋

网页版dino

仿generals小游戏(要登录)

海战棋网页版

五子棋

无尽进度条

钢琴模拟器

小小的解密游戏

猜单词

猜单词,不过是中文版

猜单词,不过是猜数字

猜单词,不过是两个一起猜

猜单词,不过是猜音符

三体2048

时间复杂度 2048

化学2048

OIER's 2048 -- AK IOI

普普通通的2048

朝代2048

人生重开普通版

人生重开爆改版(手机不能玩)

微伞小游戏

c++游戏整合

游戏合集

IO233(IO游戏集合)

yikm.net(单机闯关打类游戏)

单机创意类游戏

xingye小游戏合集

hornex.pro

SandSpiel

BapBap

植物大战僵尸

Euclidea

Natural Number Game

NandGame

Patrick's Parabox

Ztype

mc跑酷

音游

邪恶的2048

大型2048

巨型2048

博士2048

1536

扫雷3D

m28.studio(小游戏集)

Hello Minecraft! Launcher

高级小恐龙

名字竞技场

yorg

yorg3

生命游戏

FC 游戏在线玩

slay

信任的进化

The Largest Collection of Interactive Geometric Puzzles

中国最大io游戏推荐网站

解谜

猫国建设者

国际象棋

膜拜Siyuan

一个球?

HTML5游戏

小游戏集(大)

sans

人生重开模拟器

C++控制台游戏

游戏

冲浪 (edge浏览器里打开)

你画我猜

bonk.io

大游戏集

大游戏集2

大游戏集3

大游戏集4

大游戏集5

大游戏集6

MC(不是远古版本)

开挂人生

GitHub游戏

卡牌冒险者

卡牌冒险者2

1

2

3

我的世界代码 点击进入

植物大战僵尸

植物大战僵尸网页版 点击进入

超级猫里奥 点击进入

扫雷 点击进入

Removebg

今天吃啥

3D游戏

AI对话

一堆游戏

整活合集

另一堆游戏

小说:洛谷国

别人整理的DreamMC视频(本人算半个MC玩家

另一个视频合集

成就生成器

电脑模拟器

一堆游戏

去玩MineCraft

游戏

超好van的io游戏

另一堆游戏

装B模拟器

一些传统游戏

猫国建设者

Slay.one

游戏集合

比上面所有都好van的游戏

圈地大作战

SCP wiki

太空公司汉化版

国际象棋Online

人生重开模拟器

Nicky case-信任的进化

Nicky case-多边形的故事

超苦逼冒险者

Evlove-进化

C++游戏

游戏合集

地铁线路图绘制器

Minecraft 成就编辑器

Three 2048

我的世界在线玩

网页版我的世界

枪战

人生重开模拟器(大全

C++小游戏

9007199254740992(2048++版

洛谷树

正常的2048

生命小游戏

稍微卡一点但是很好玩

Game集合1

让人玩到自闭的尺规作图游戏(Eulidea)

Game集合2

网络答题(?)

飞翔的小鸟

国际象棋

球球大作战

膜拜Si yuan

象棋

切水果

五子棋

吃豆人

太空战机

高级马里奥(???)

视觉体验

各种小挂机(可摘到博客)

游戏主站

Crazygames

在线DOS游戏

小霸王其乐无穷

Pacogames

Kbhgames

畅玩空间

io Games

Y8 Games

Sans Fight

切断它

过关算我输

大全

大全2

超多可玩小游戏

音游

专业表白

炫彩粒子

C++小游戏

游戏大全

https:\/\/florrio.fandom.com\/zh\/wiki\/%E7%AD%96%E7%95%A5

数墙

合集

写算法的小女孩

这个其实更好玩

猜汉字

2048

这个真的很好玩

问问自己

球球

酷跑

插死它

玩个锤子

圈地

好van的

跳跳

算数入门

更多游戏(比这个更多)

沙盒游戏

考验智商的游戏

谷歌小恐龙升级版

一堆优质C++小游戏

聪明人才能通关的游戏 || 攻略

用键盘弹钢琴

玩物理大师专属游戏(超好玩)

为其他物理大师设计游戏

一堆散装游戏

一个放了很多游戏链接的个人主页

策略空战游戏

太空放置游戏

一堆好游戏

人生重开模拟器附赠破解版)&另一个破解版

另一个版本的人生重开模拟器

墙裂推荐游戏(不能存档)

小io的游戏集

HTML5网页游戏HTML5

加法版2048

元素版2048

加大版2048

冒险者

解压水果合成游戏

C++小游戏(特别多)

另一坨C++小游戏

装13必备黑客模拟器

一堆奇怪的链接

一个奇怪的云剪贴板

一个神秘的游戏

2.

4.

7. https:\/\/zhuanlan.zhihu.com\/p\/483562115

https:\/\/note.youdao.com\/ynoteshare\/index.html?id=978ab1aebc4b827dc14083a6469b8869&type=note&_time=1680677945610

共 20 篇博客