noip 2018 模拟赛15

T 1 T_1 T1——slon(1783)

Description:

对于一个表达式 S S S,形如 5 + x ∗ ( 3 + 2 ) , x + 3 ∗ x + 4 ∗ ( 5 + 3 ∗ ( 2 + x − 2 ∗ x ) ) 5+x*(3+2),x+3*x+4*(5+3*(2+x-2*x)) 5+x(3+2),x+3x+4(5+3(2+x2x)),求最小的非负数 x x x,使得表达式的值对 M M M取模后为 P P P
注意: x x x的指数不超过 1 1 1,且一个运算符号的两边一定有操作数。
∣ S ∣ ≤ 1 0 5 , 0 ≤ P ≤ M − 1 , M ≤ 1 0 6 |S|\le10^5,0\le P\le M-1,M\le 10^6 S105,0PM1,M106

Solution:

  • 表达式求值模拟题(沙雕模拟题
  • 做这种表达式求值问题,无非就是栈模拟,以及重载运算符和写一些关键函数,可以大大缩短码量和实现难度。
  • 最后,要求对 M M M取模的情况,但发现此题的 M M M比较小,那么直接 f o r for for找即可(扩展欧几里得)。

Code:

#include<bits/stdc++.h>
using namespace std;
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)

const int N=100002;

int n,P,mod;
int Top,top;
char op[N],str[N];

struct node{
	int k,b;
}Stk[N];

node operator+(node x,node y){
	node res;
	res.k=(x.k+y.k)%mod;
	res.b=(x.b+y.b)%mod;
	return res;
}

node operator-(node x,node y){
	node res;
	res.k=(x.k-y.k)%mod; 
	res.b=(x.b-y.b)%mod; 
	return res;
}

node operator*(node x,node y){
	node res;
	res.k=(1ll*x.k*y.b+1ll*x.b*y.k)%mod;
	res.b=1ll*x.b*y.b%mod;
	return res;
}

node calc(node A,node B,char c){
	if(c=='*') return A*B;
	if(c=='+') return A+B;
	if(c=='-') return A-B;
}

bool Cmp(char a,char b){
	if(a=='(' or b=='(') return 0;
	if(b==')' or b=='+' or b=='-' or a=='*') return 1;
	return 0;
}

int main(){
//	freopen("slon.in","r",stdin);
//	freopen("slon.out","w",stdout);
	scanf("%s%d%d",str,&P,&mod);
	int n=strlen(str);

	str[n]=')';op[top++]='(';
	
	SREP(i,0,n+1){
		if(str[i]=='x') Stk[Top++]=(node){1,0};
		
		else if(str[i]>='0' and str[i]<='9'){
			int x=(str[i]-'0')%mod;
			while(str[i+1]>='0' and str[i+1]<='9') x=(x*10+str[++i]-'0')%mod;
			Stk[Top++]=(node){0,x};
		}
		
		else{
			while(Cmp(op[top-1],str[i])){
				node x=Stk[--Top],y=Stk[--Top];
				char c=op[--top];
				Stk[Top++]=calc(y,x,c);
			}
			if(str[i]==')') --top;
			else op[top++]=str[i];
		}
	}
	
	node ans=Stk[--Top];
	SREP(i,0,mod){
		if((1ll*ans.k*i+ans.b-P)%mod==0){
			printf("%d\n",i);
			break;
		}
	}
	return 0;
}

T 2 T_2 T2——bad(3835)

Description:

有一个长度为 n n n的序列 A A A
定义一个坏对 ( i , j ) (i,j) (i,j)当且仅当 i &lt; j , A i m o d &ThinSpace;&ThinSpace; A j = K i&lt;j,A_i \mod A_j=K i<j,AimodAj=K
求有多少个区间满足区间不存在坏对。
n , A i ≤ 1 0 5 , 0 ≤ K ≤ 1 0 5 n,A_i\le 10^5,0\le K \le 10^5 n,Ai105,0K105

Solution:

  • 首先,坏对的定义,我们可以去掉模数,即 ( i , j ) , i &lt; j , A i = A j ∗ x + K (i,j),i&lt;j,A_i=A_j*x+K (i,j),i<j,Ai=Ajx+K
  • 又因为 A i ≤ 1 0 5 A_i \le 10^5 Ai105,那么,我们可以预处理出与 i i i匹配坏对的数
  • 那么我们在遍历时,对于第 i i i个数,我们要更新与之可以匹配坏对的数
  • 那么对答案的贡献即为 i − l a s t i-last ilast
  • 这样复杂度为 Θ ( n ⋅ n ) \Theta(n\cdot \sqrt n) Θ(nn )

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define ll long long
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}

template<class T>inline void rd(T &x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=1e5+2;

int n,m;

int A[N];

int tot;
struct node{
	int l,r;
	bool operator<(const node &_)const{
		return r<_.r;
	}
}B[N];

struct p50{
	void solve(){
		tot=0;
		REP(i,2,n) REP(j,1,i) if(A[j]%A[i]==m) B[++tot]=(node){j,i};
		sort(B+1,B+1+tot);
		int ans=n;
		REP(l,1,n) REP(r,l+1,n) {
			bool flag=1;
			REP(i,1,tot) {
				if(r<B[i].l)break;
				if(l<=B[i].l and B[i].r<=r){flag=0;break;}
			}
			ans+=flag;
		}
		printf("%d\n",ans);
	}
}p1;

struct p70{
	vector<int>Son[N];
	int mx[N];
	void solve(){
		SREP(i,1,N) for(int j=i;j<N;j+=i) Son[j].push_back(i);
		ll ans=0;
		int last=0;
		REP(i,1,n){
			chkmax(last,mx[A[i]]);
			ans+=i-last;
			SREP(j,0,Son[A[i]].size()) chkmax(mx[Son[A[i]][j]],i);
		}
		printf("%lld\n",ans);
	}
}p2;

struct p100{
	
	vector<int>Son[N];
	int mx[N];
	
	void solve(){
		SREP(i,m+1,N) for(int j=0;j+m<N;j+=i)Son[j+m].push_back(i);
		ll ans=0;
		int last=0;
		REP(i,1,n){
			if(A[i]>m) chkmax(last,mx[A[i]]);
			ans+=i-last;
			SREP(j,0,Son[A[i]].size()) chkmax(mx[Son[A[i]][j]],i); 
		}
		printf("%lld\n",ans);
	}
}p3;

int main(){
//	freopen("bad.in","r",stdin);
//	freopen("bad.out","w",stdout);
	rd(n),rd(m);
	REP(i,1,n) rd(A[i]);
	
	if(n<=1000)p1.solve();
	else if(!m)p2.solve();
	else p3.solve();
	return 0;
}

T 3 T_3 T3——holiday(3064)

Description:

有一排城市,编号为0~n-1,对于城市 i i i,只与城市 i − 1 i-1 i1和城市 i + 1 i+1 i+1相连,且每个城市有一个价值 A i A_i Ai
现在从城市 s s s出发,有 d d d天时间。
规定相邻城市之间的花费和得到一个城市的价值的花费为 1 1 1天。
求最大价值。
2 ≤ n ≤ 1 0 5 , 0 ≤ A i ≤ 1 0 9 , 0 ≤ s ≤ n − 1 , 0 ≤ d ≤ 2 ⋅ n + n 2 2\le n\le10^5,0\le A_i\le10^9,0\le s\le n-1,0\le d \le 2\cdot n+\frac{n}{2} 2n105,0Ai109,0sn1,0d2n+2n

Solution:

  • 写部分分时,不难发现有单调性,对于每个起点 l l l,都有最优解的终点 r r r
  • 那么可以分治一个区间,进而枚举左端点,来找最优解的右端点,再用主席树来查询当前区间的贡献
  • 复杂度为 Θ ( n log ⁡ 2 n ) \Theta(n\log ^2n) Θ(nlog2n)

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define LL long long
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}

const int N=1e5+2,INF=0x3f3f3f3f;

int n,s,d;
int A[N];

struct p30{
	void solve(){
		LL ans=0;
		SREP(S,0,1<<n){
			LL val=0;
			int sum=0;
			int l=s,r=s;
			SREP(i,0,n) if(S&(1<<i)) {
				if(i<=s) chkmin(l,i);
				if(i>=s) chkmax(r,i);
				sum++;
				val+=A[i];
			}
			
			if(l!=INF and r!=-INF) {
				if(s-l<r-s) sum+=(s-l)*2+r-s;
				else sum+=s-l+2*(r-s);
			}
			if(sum<=d)chkmax(ans,val);
		}
		printf("%lld\n",ans);
	}
}p1;

struct p50{
	void solve(){
		
	}
}p2;

struct p70{
	priority_queue<int>Q;
	void solve(){
		LL res=0,ans=0;
		SREP(i,0,n){
			Q.push(-A[i]);res+=A[i];
			if(i>=d)break;
			while(i+Q.size()>d)res+=Q.top(),Q.pop();
			chkmax(ans,res);
		}
		printf("%lld\n",ans);
	}
}p3;

struct p100{
	
	int B[N],tot;
	
	int Lson[N*20],Rson[N*20],root[N],tim;
	int cnt[N*20];
	LL sum[N*20];
	
	void Update(int &p,int f,int l,int r,int x){
		p=++tim;
		if(l==r){
			cnt[p]=cnt[f]+1;
			sum[p]=sum[f]+B[x];
			return;
		}
		int mid=(l+r)>>1;
		Lson[p]=Lson[f],Rson[p]=Rson[f];
		
		if(x<=mid) Update(Lson[p],Lson[f],l,mid,x);
		else Update(Rson[p],Rson[f],mid+1,r,x);
		
		cnt[p]=cnt[Lson[p]]+cnt[Rson[p]];
		sum[p]=sum[Lson[p]]+sum[Rson[p]];
	} 
	
	LL Query(int p,int f,int k,int l,int r){
		if(l==r)return 1ll*B[l]*min(k,cnt[p]);
		int mid=(l+r)>>1;
		int tmp=cnt[Rson[p]]-cnt[Rson[f]];
		if(k<=tmp) return Query(Rson[p],Rson[f],k,mid+1,r);
		else return sum[Rson[p]]-sum[Rson[f]]+Query(Lson[p],Lson[f],k-tmp,l,mid);
	}
	
	LL ans;
	
	void Solve(int ll,int lr,int rl,int rr){
		int rmid=(rl+rr)>>1,lmid=lr;
		LL Mx=0;
		REP(i,ll,lr){
			int res=(rmid-i)+min(s-i,rmid-s);
			if(d-res>0) if(chkmax(Mx,Query(root[rmid],root[i-1],d-res,1,tot))) lmid=i;
		}
		chkmax(ans,Mx);
		if(rl<=rmid-1) Solve(ll,lmid,rl,rmid-1);
		if(rmid+1<=rr) Solve(lmid,lr,rmid+1,rr);
	}
	
	void solve(){
		SREP(i,0,n)B[++tot]=A[i];
		sort(B+1,B+1+tot);
		tot=unique(B+1,B+1+tot)-B-1;
		DREP(i,n,1) A[i]=lower_bound(B+1,B+1+tot,A[i-1])-B;
		REP(i,1,n) Update(root[i],root[i-1],1,tot,A[i]);
		s++;
		Solve(1,s,s,n);
		printf("%lld\n",ans);
	}
}p4;

int main(){
//	freopen("holiday.in","r",stdin);
//	freopen("holiday.out","w",stdout);
	scanf("%d%d%d",&n,&s,&d);
	SREP(i,0,n) scanf("%d",&A[i]);
	
	if(n<=20)p1.solve();
//	else if(n<=3000)p2.solve();
	else if(!s)p3.solve();
	else p4.solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值