8.29

最后一天在高中自习

下午一到调了 \(1.5h\) 的大模拟

就是昨天那道

然后得了 \(55pts\)

继续放代码尸体

CODE
#include<bits/stdc++.h>
#define fst first
#define sec second
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<LL,int> pii;
const int maxn=155;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
void read(LL& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
struct edge{
	string name;
	int to;
};
pii d[maxn];
vector<edge> mp[maxn];
unordered_map<string,int> m;
unordered_map<string,pii> var;
set<pii> va;
int n;
int tot=0;
LL len=0;
LL mceil(LL x,LL y){
	if(x%y==0) return x/y;
	else return x/y+1;
}
int main(){
	freopen("P9754_7.in","r",stdin);
	freopen("wwa.out","w",stdout);
	read(n);
	int op,p;
	string s,a,b;
	LL ar;
	m["byte"]=++tot,d[tot]=mkp(1,1);
	m["short"]=++tot,d[tot]=mkp(2,2);
	m["int"]=++tot,d[tot]=mkp(4,4);
	m["long"]=++tot,d[tot]=mkp(8,8);//空间大小+对齐要求
	while(n--){
		read(op);
		if(op==1){
			cin>>s;
			m[s]=++tot;
			read(p);
			LL lst=0;
			d[tot].sec=0;
			while(p--){
				cin>>a>>b;
				mp[tot].push_back((edge){b,m[a]});
				d[tot].sec=max(d[tot].sec,d[m[a]].sec);
				lst=mceil(lst,d[m[a]].sec)*d[m[a]].sec+d[m[a]].fst;
			}
			d[tot].fst=lst;
			//cout<<"ans=";
			printf("%lld %d\n",d[tot].fst,d[tot].sec);
		}
		if(op==2){
			cin>>a>>b;
			var[b]=mkp(mceil(len,d[m[a]].sec)*d[m[a]].sec,m[a]);
			va.insert(var[b]);
			len=var[b].fst+d[m[a]].fst;
			//cout<<"ans=";
			printf("%lld\n",var[b].fst);
		}
		if(op==3){
			cin>>s;
			//cout<<"ans=";
			s.append(1,'.');
			int k=0;
			a.clear();
			while(s[k]!='.') a.append(1,s[k++]);
			int now=var[a].sec;
			LL ans=var[a].fst;
			a.clear();
			for(int i=k+1;i<(int)s.size();i++){
				if(s[i]=='.'){
					for(int j=0;j<(int)mp[now].size();j++){
						int v=mp[now][j].to;
						if(mp[now][j].name==a){
							ans=mceil(ans,d[v].sec)*d[v].sec;
							now=v;
							break;
						}
						else{
							ans=mceil(ans,d[v].sec)*d[v].sec+d[v].fst;
						}
					}
					a.clear();
				}
				else{
					a.append(1,s[i]);
				}
			}
			if(ans==1516||ans==2536) cout<<"wa::";
			printf("%lld\n",ans);
		}
		if(op==4){
			read(ar);
			//cout<<"ans=";
			vector<string> v;
			LL l=0;
			int now=0;
			for(auto i=va.begin();i!=va.end();i++){
				if(i->fst<=ar) l=i->fst,now=i->sec;
				else break;
			}
			for(auto i=var.begin();i!=var.end();i++){
				if(i->sec.fst==l){
					v.push_back(i->fst);
					break;
				}
			}
			if(!now){
				cout<<"ERR\n";
				continue;
			}
			while(now>4){
				int tp=-1;
				for(int j=0;j<(int)mp[now].size();j++){
					int v=mp[now][j].to;
					LL li=mceil(l,d[v].sec)*d[v].sec+d[v].fst;
					if(li>ar){
						l=mceil(l,d[v].sec)*d[v].sec,tp=j;
						break;
					}
					else{
						l=li;
					}
				}
				if(tp<0){
					l=-1e18;
					break;
				}
				v.push_back(mp[now][tp].name);
				now=mp[now][tp].to;
			}
			//cout<<now<<' '<<ar-l+1<<endl;
			l=mceil(l,d[now].sec)*d[now].sec;
			if(l>ar||ar-l>=d[now].fst){
				cout<<"ERR";
			}
			else{
				cout<<v[0];
				for(int i=1;i<(int)v.size();i++){
					cout<<'.'<<v[i];
				}
			}
			putchar('\n');
		}
	}
	return 0;
}
//^o^

然后打算去打比赛,把四题看了一遍

大概都是比较新的题型

最近还是以备考CSP为主,所以又写真题去了

P8818

简单博弈论,但实现有难度

先手要积最大,讨论以下几种情况

一,后手只有正数

1.先手没有正数,那么选最大的负数,后手选最大的正数

2.先手有正数,那么选最大的正数,后手选最小的正数

二,后手只有负数

1.先手没有负数,那么选最小的正数,后手选最大的正数

2.先手有负数,那么选最小的负数,后手选最大的正数

二,后手既有正数,又有负数

1.若先手有负数,则可以选最大的负数,后手选最大的正数

2.若先手有正数,则可以选最小的正数,后手选最小的负数

两者中取其乘积最大者

再考虑有 \(0\) 的情况:

当先手有 \(0\) 的时候

此时若出现无论先手其他的任何数,后手都能使其变为负数时,使用 \(0\)

当后手有 \(0\) 的时候

此时若出现先手旋完后,后手无论接其他的任何数都会使积变为正数,使用 \(0\)

发现我们只需要把 \(0\) 当作最大的负数,最小的正数,就可以在上面的分类讨论中解决 \(0\) 的问题了

也就是说,需要假定 \(0\) 既是正数,也是负数

综上,我们只需要求区间的负数最大,最小,正数最大,最小值即可

用st表或者线段树都可以

此处使用线段树

CODE
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=2.1e5+5;
const int inf=2e9+1;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
int t1[maxn<<2],t2[maxn<<2],t3[maxn<<2],t4[maxn<<2];
//正数中的最小值,最大值,负数中的最大值,最小值
int n,m,q;
int a[maxn];
void push_up(int u){
	t1[u]=min(t1[u<<1],t1[u<<1|1]);
	t2[u]=max(t2[u<<1],t2[u<<1|1]);
	t3[u]=max(t3[u<<1],t3[u<<1|1]);
	t4[u]=min(t4[u<<1],t4[u<<1|1]);
}
void build(int l=1,int r=n+m,int u=1){
	if(l==r){
		if(a[l]>=0) t1[u]=a[l],t2[u]=a[l];
		if(a[l]<=0) t3[u]=a[l],t4[u]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,u<<1),build(mid+1,r,u<<1|1);
	push_up(u);
}
int query1(int L,int R,int l=1,int r=n+m,int u=1){
	if(r<L||l>R) return inf;
	if(l>=L&&r<=R) return t1[u];
	int mid=(l+r)>>1;
	return min(query1(L,R,l,mid,u<<1),query1(L,R,mid+1,r,u<<1|1));
}
int query2(int L,int R,int l=1,int r=n+m,int u=1){
	if(r<L||l>R) return -inf;
	if(l>=L&&r<=R) return t2[u];
	int mid=(l+r)>>1;
	return max(query2(L,R,l,mid,u<<1),query2(L,R,mid+1,r,u<<1|1));
}
int query3(int L,int R,int l=1,int r=n+m,int u=1){
	if(r<L||l>R) return -inf;
	if(l>=L&&r<=R) return t3[u];
	int mid=(l+r)>>1;
	return max(query3(L,R,l,mid,u<<1),query3(L,R,mid+1,r,u<<1|1));
}
int query4(int L,int R,int l=1,int r=n+m,int u=1){
	if(r<L||l>R) return inf;
	if(l>=L&&r<=R) return t4[u];
	int mid=(l+r)>>1;
	return min(query4(L,R,l,mid,u<<1),query4(L,R,mid+1,r,u<<1|1));
}
int main(){
	read(n),read(m),read(q);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	for(int i=n+1;i<=n+m;i++){
		read(a[i]);
	}
	int s=(n+m+10)<<2;
	fill(t1,t1+s,inf);
	fill(t2,t2+s,-inf);
	fill(t3,t3+s,-inf);
	fill(t4,t4+s,inf);
	build();
	int l1,r1,l2,r2;
	while(q--){
		read(l1),read(r1),read(l2),read(r2);
		l2+=n,r2+=n;
		LL ans=0;
		//query1正数最小值
		//query2正数最大值
		//query3负数最大值
		//query4负数最小值
		if(query3(l2,r2)==-inf){//对面没有负数,即全是正数
			if(query1(l1,r1)==inf){//我没有正数,即全是负数
				ans=1ll*query3(l1,r1)*query2(l2,r2);
				//负数最大*正数最大
			}
			else{
				ans=1ll*query2(l1,r1)*query1(l2,r2);
				//正数最大*正数最小
			}
		}
		else if(query2(l2,r2)==-inf){//对面没有正数,即全是负数
			if(query4(l1,r1)==inf){//我没有负数,即全是正数
				ans=1ll*query1(l1,r1)*query4(l2,r2);
				//正数最小*负数最小
			}
			else{
				ans=1ll*query4(l1,r1)*query3(l2,r2);
				//负数最小*负数最大
			}
		}
		else{
			ans=-(1ll*inf*inf);
			if(query1(l1,r1)!=inf){//我有正数
				ans=max(ans,1ll*query1(l1,r1)*query4(l2,r2));
			}
			if(query4(l1,r1)!=inf){//我有负数
				ans=max(ans,1ll*query3(l1,r1)*query2(l2,r2));
			}
			//正数最小*负数最小,负数最大*正数最大
		}
		printf("%lld\n",ans);
	}
	return 0;
}
//^o^

虽然理论复杂度上,st表优于线段树

但是最优解竟然是线段树的!!!

P9744

题目

对于一个要变成 \(1\) 的位置,要么将他前面的全变成 \(0\),然后把前面所有的位置都变成 \(1\)

要么找一个比他靠后的位置,把其前面的全变成 \(0\) ,然后把该位置和其前面的所有要变 \(1\) 的地方全变成 \(1\)

要么从前一个变成 \(1\) 的地方继承过来,把两个 \(1\) 中间的部分变成 \(0\)

这样就有状态转移了,所以就是个 \(dp\)

但是注意把一个位置前的所有东西变成 \(0\) 不一定是操作 \(1\) 的原数

有可能通过操作 \(1\) 和操作 \(2\) 一起进行得到

所以先 \(dp\) 一遍把一个位置前的所有元素变成一的最少花费

CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=5e5+5;
void read(int& x){
	char c;
	while((c=getchar())<48);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
}
void read(LL& x){
	char c;
	while((c=getchar())<48);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
}
int a[maxn],b[maxn],c[maxn];
int f[maxn],v[maxn];
LL s[maxn];
int n,m,q;
int main(){
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++) read(b[i]);
	for(int i=1;i<=n;i++) read(c[i]);
	for(int i=1;i<=n;i++){
		f[i]=min(a[i],f[i-1]+b[i]);
		s[i]=s[i-1]+b[i];
	}
	read(q);
	LL ans,sum;
	while(q--){
		read(m);
		for(int i=1;i<=m;i++){
			read(v[i]);
		}
		ans=0,sum=0;
		for(int i=1;i<=m;i++){
			ans=min({f[v[i]]+sum+c[v[i]],f[v[i]-1]+sum,ans+s[v[i]-1]-s[v[i-1]]});
			sum+=c[v[i]];
		}
		printf("%lld\n",min(ans+s[n]-s[v[m]],f[n]+sum));
	}
	return 0;
}
//^o^

今天还去看了团队的题,做了挺久的实验,具体不能透露

说不定是明年某场洛谷团队赛里的题呢?

posted @ 2025-08-29 21:03  huangems  阅读(4)  评论(0)    收藏  举报