恩偶爱皮模拟尸体-29

水博客太快乐了。。。

RT

好几场模拟赛的博客都因为懒所以被咕掉了,也不打算补,就直接从新的比赛开始水吧。。。

因为用了 \(HZ\) 的电脑,换了输入法,所以标题也和原来不一样了。。。。

考场

先大概地看了一下三道题,发现 \(T3\) 可以大力树剖线段树,于是想也不想就开始写了。。。

写了大概一个小时,发现想法是错的。。。
但是在原代码的基础上稍微改一下就可以了。。。
于是稍微改了一下。。。过了第三个大样例,然而第二个大样例挂了。。。。。
这句号好难看。。。
其实有个非常简单的情况没有考虑到。。。。
但是考场上也没看出来,就这样吧。。。

回头看 \(T1\) \(T2\)\(T1\) 着实没什么思路,直接暴力,感觉膜数这么小有什么玄机。。。但是也没有深思。。。。

然后看 \(T2\) ,乍一看就像个同余最短路的样子。。。
这句号好难看。。。
于是大力同余最短路,然而在同余最短的的时候因为没有考虑到 \(C\) 对答案的限制,所以一定会有少考虑的情况,但因为最后时间不够了,想也没想就交了。。。。

分数

\(t1\ 20pts\ +\ t2\ 90pts\ +\ t3\ 80pts\ =\ 190pts\)
发现 \(t2\) 居然有 \(90pts\) ???
震惊,这明明就是个假算法啊喂!!!
\(t3\) 少考虑一种情况挂成了 \(80pts\) 。。。
实际上是非常简单的情况。。。然而我没有想到。。。

题解

A. 最长不下降子序列

膜数非常小,因此一定会有循环节且长度很短。。。
而在选择最长不下降子序列时,每个循环节至少会选择一个点,因此只要在至多 \(D\) 个循环节中选出最长不下降子序列,再在没有选到的每一个循环节中选则一个数即可。。

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+10, D=200;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, t0, a, b, c, d, len, ans;
int num[N], vis[N], st, ul;
int up[N], low[N];
class TRE{
private :
	int a[N];
	inline int lb(int x) { return x&-x; }
public :
	inline void add(int p, int x) { for(; p<D; p+=lb(p)) a[p]=max(a[p], x); }
	inline int query(int p) { int ans=0; for(; p; p-=lb(p)) ans=max(ans, a[p]); return ans; }
}t;
signed main(void){
	n=read(); t0=read();
	a=read(), b=read(), c=read(), d=read();
	vis[num[1]=t0]=1;
	for(int i=2; i<=n; ++i){
		num[i]=(num[i-1]*num[i-1]*a+num[i-1]*b+c)%d;
		if(vis[num[i]]) { len=i-vis[num[i]]; st=i-len; break; }
		vis[num[i]]=i;
	}
	if(len) ul=(n-st+1)/len;
	if(ul<=len){
		t.add(t0+1, 1);
		for(int i=2; i<=n; ++i){
			num[i]=(num[i-1]*num[i-1]*a+num[i-1]*b+c)%d;
			t.add(num[i]+1, t.query(num[i]+1)+1);
		}
		printf("%lld\n", t.query(max(t0+1, d))); return 0;
	}
	t.add(t0+1, 1);
	for(int i=2; i<=len*d+st-1+((n-st+1)%len); ++i){
		num[i]=(num[i-1]*num[i-1]*a+num[i-1]*b+c)%d;
		t.add(num[i]+1, t.query(num[i]+1)+1);
	}
	printf("%lld\n", t.query(max(t0+1, d))+ul-d);
	return 0;
}

B. 完全背包问题

继续考虑同余最短路。。。
若不会同余最短路,可以看一下这个完全是为了推销博客而出现的链接。。。
如果直接在u原数据上跑同余最短路,显然没有办法考虑 \(C\) 对答案的限制,因此考虑将图分层,更改原来同余最短路的定义,设 \(dis_{i,j}\) 表示用了 \(i\) 个大于等于 \(l\) 的物品,在膜 \(D\) 意义下为 \(j\) 的最小值。。。
然后直接跑分层图最短路,对于每个询问 \(O(C)\) 判断是否合法即可。。。

code
#include<bits/stdc++.h>
using namespace std;
#define ul first
#define num second
#define int long long
const int N=1e4+10, C=40;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, m, l, c;
int v[N], dis[N][C];
bool vis[N][C];
queue<pair<int, int> > q;
void spfa(){
	memset(dis, 0x3f3f, sizeof dis);
	dis[0][0]=0;
	q.push(make_pair(0, 0));
	while(!q.empty()){
		pair<int, int > u=q.front(); q.pop(); vis[u.ul][u.num]=0;
		for(int i=2; i<=n; ++i){
			if(v[i]>=l&&u.num==c) break;
			if(dis[(v[i]+u.ul)%v[1]][u.num+1]>dis[u.ul][u.num]+v[i]&&v[i]>=l){
				dis[(v[i]+u.ul)%v[1]][u.num+1]=dis[u.ul][u.num]+v[i];
				if(!vis[(v[i]+u.ul)%v[1]][u.num+1]) q.push(make_pair((v[i]+u.ul)%v[1], u.num+1)), vis[(v[i]+u.ul)%v[1]][u.num+1]=1;
			}
			if(v[i]<l&&dis[(v[i]+u.ul)%v[1]][u.num]>dis[u.ul][u.num]+v[i]){
				dis[(v[i]+u.ul)%v[1]][u.num]=dis[u.ul][u.num]+v[i];
				if(!vis[(v[i]+u.ul)%v[1]][u.num]) q.push(make_pair((v[i]+u.ul)%v[1], u.num)), vis[(v[i]+u.ul)%v[1]][u.num]=1;
			}
		}
	}
}
signed main(void){
	n=read(), m=read();
	for(int i=1; i<=n; ++i) v[i]=read();
	sort(v+1, v+1+n); int x, num=0;
	l=read(), c=read(); spfa();
	bool flag;
	while(m--){
		x=read(); flag=0;
		for(int i=c; ~i; --i) if(x>=dis[x%v[1]][i]) { flag=1; break; }
		flag ? puts("Yes") : puts("No");
		num+=!flag;
	}
	return 0;
}

又好写跑的又快。。。

C. 最近公共祖先

直接大力树剖线段树,对于每一个修改操作,将这个点到根节点的链进行修改,每一个查询也将这个点到根节点的链进行查询。。。
一堆细节要考虑,懒得写了。。。

code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10, INF=0x7fffffff;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, m, w[N], num2[N], to[N];
vector<int > l[N], l1[N];
int dep[N], fa[N], siz[N], top[N], son[N], num[N], num1[N], ind;
bool ul, vis[N];
char ch[20];
struct TRE{
	int l, r, maxn;
	bool ul;
}t[N<<2];
void built(int l, int r, int p){
	t[p].l=l, t[p].r=r;
	if(l==r) return;
	int mid=(l+r)>>1;
	built(l, mid, p<<1); built(mid+1, r, p<<1|1);
}
void change(int l, int r, int p){
	if(l>r) return;
	if(l<=t[p].l&&r>=t[p].r) { t[p].ul=1; return; }
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid) change(l, r, p<<1);
	if(r>mid) change(l, r, p<<1|1);
}
void insert(int x, int w1, int p){
	if(t[p].l==t[p].r) { t[p].maxn=w1; return; }
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid) insert(x, w1, p<<1);
	else insert(x, w1, p<<1|1);
	t[p].maxn=max(t[p<<1].maxn, t[p<<1|1].maxn);
}
int que(int l, int r, int p){
	if(l>r) return 0;
	if(l<=t[p].l&&r>=t[p].r) return t[p].maxn;
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid&&r>mid) return max(que(l, r, p<<1), que(l, r, p<<1|1));
	if(l<=mid) return que(l, r, p<<1);
	return que(l, r, p<<1|1);
}
bool check(int x, int p){
	if(t[p].ul) return 1;
	if(t[p].l==t[p].r) return 0;
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid) return check(x, p<<1);
	return check(x, p<<1|1);
}
void dfs1(int u, int f){
	siz[u]=1, dep[u]=dep[f]+1, fa[u]=f;
	for(int v : l[u]){
		if(v==f) continue;
		dfs1(v, u); siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u, int tp){
	top[u]=tp; num[u]=++ind, num1[ind]=u;
	if(son[u]) dfs2(son[u], tp);
	for(int v : l[u]) if(v!=fa[u]&&v!=son[u]) dfs2(v, v);
}
inline void modify(int u){
	while(u){
		change(num[top[u]], num[u]-1, 1);
		insert(num[u], w[u], 1);
		if(to[fa[top[u]]]!=top[u]) num2[fa[top[u]]]++;
		to[fa[top[u]]]=top[u];
		u=fa[top[u]];
	}
}
inline int query(int u){
	int ans=-INF, v=0;
	if(vis[u]) ans=w[u];
	while(u){
		ans=max(ans, que(num[top[u]], num[u]-1, 1));
		if(num2[u]>1||(to[u]!=v&&num2[u])||check(num[u], 1)||vis[u]) ans=max(ans, w[u]);
		v=top[u], u=fa[v];
	}
	return ans;
}
signed main(void){
	n=read(), m=read(); int x, y;
	for(int i=1; i<=n; ++i) w[i]=read();
	for(int i=1; i<n; ++i){
		x=read(), y=read();
		l[x].push_back(y);
		l[y].push_back(x);
	}
	dfs1(1, 0), dfs2(1, 1); built(1, n, 1);
	while(m--){
		scanf("%s", ch+1); x=read();
		if(ch[1]=='Q'){
			if(!ul) { printf("-1\n"); continue; }
			printf("%d\n", query(x));
		}else modify(x), ul=1, vis[x]=1;
	}
	return 0;
}

也可以不树剖,直接在 \(dfs\) 序上乱搞,可以少一个 \(log\) 。。。

posted @ 2021-08-03 17:48  Cyber_Tree  阅读(53)  评论(2编辑  收藏  举报