2020.03.21日常总结——思维题与全面的思考

思维题——洛谷P4588     [TJOI2018]数学计算 \color{green}{\text{思维题——洛谷P4588\ \ \ \ \ [TJOI2018]数学计算}} 思维题——洛谷P4588     [TJOI2018]数学计算

【题目】: \color{blue}{\text{【题目】:}} 【题目】: 你有一个数 x x x,初始为 1 1 1。你有两种操作,分别为:

    1. 给定一个数 m m m,把 x x x 变成 x × m x \times m x×m,然后输出 x x x mod \text{mod} mod 取模的值。
    1. 给定一个数 t t t,把 x x x 变为 x / x/ x/ t t t 次操作所乘的数。如第 t t t 次操作所乘数为 2 2 2,则把 x x x 变为 x 2 \dfrac{x}{2} 2x。数据保证第 t t t 次操作一定是操作 1 1 1,且每个操作最多被除一次,即保证 x x x 在任何时候都是一个整数。操作后,输出 x x x mod \text{mod} mod 取模的值。

【思路】: \color{blue}{\text{【思路】:}} 【思路】: 直接模拟会因为爆 long long 的问题导致代码非常复杂,甚至无法编写。

考虑强大的数据结构——线段树。建立一棵线段树,其叶子节点都是对于的乘数,每个非叶子节点的值为其左右儿子的值的乘积对 mod \text{mod} mod 取模的值。这样,任意时候都有 x = x= x= 该线段树的根的值。

操作 1 1 1 可以直接上,操作 2 2 2 可以看做是把第 t t t 次的乘数改为 1 1 1。因此,我们只需要打一个线段树修改即可。

【 代 码 】 : \color{blue}{【代码】:}

const int N=1e5+100;
#define ll long long
ll mod;int tot,G[N];
int test_number,q;
struct Segment_tree{
	ll sum[N<<2];//记得4倍空间 
	inline void pushup(int o){
		sum[o]=sum[o<<1]*sum[o<<1|1]%mod;
	}
	inline void build(int o,int l,int r){
		if (l==r){sum[o]=1ll;return;}
		register int mid=(l+r)>>1;
		build(o<<1|1,mid+1,r);
		build(o<<1,l,mid);
		pushup(o);return;
	}
	void updata(int o,int l,int r,int p,ll v){
		if (l==r){sum[o]=v;return;}
		register int mid=(l+r)>>1;
		if (p<=mid) updata(o<<1,l,mid,p,v);
		else updata(o<<1|1,mid+1,r,p,v);
		pushup(o);return;
	}
}SGT;
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
	char c=0;ll x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
namespace fast_write{
	void write(ll a,bool b){
		if (a==0){
			if (b) putchar('0');
		}
		else{
			write(a/10,false);
			putchar(a%10+'0');
		}
	}
	void print(ll a,char c){
		write(a,true);
		putchar(c);
	}
}
int main(){
	test_number=read();
	while (test_number--){
		q=read();mod=read();
		SGT.build(1,1,q);tot=0;
		memset(G,0,sizeof(G));
		for(int i=1;i<=q;i++){
			int opt=read();ll t=read();
			if (opt==2) SGT.updata(1,1,q,G[t],1);
			else SGT.updata(1,1,q,G[i]=(++tot),t%mod);
			fast_write::print(SGT.sum[1]%mod,'\n');
		}
	}
	return 0;
}

洛谷P1575     正误问题 \color{green}{\text{洛谷P1575\ \ \ \ \ 正误问题}} 洛谷P1575     正误问题

【 链 接 】 : \color{blue}{【链接】:} https://www.luogu.com.cn/problem/P1575

【 思 路 】 : \color{blue}{【思路】:} 很简单,直接用一个栈模拟即可。如果 not 后面不是 truefalse 的话,就输出 error

#include 
using namespace std;
stack<bool> num;
stack<string> oper;
int cti(string s){
	if (s=="or") return 1;
	if (s=="and") return 2;
	if (s=="not") return 3;
	return 0;
} 
inline bool calc(){
	register bool x,y;
	if (num.size()){
		y=num.top();num.pop();
	}
	else return false;
	if (num.size()){
		x=num.top();num.pop();
	}
	else return false;
	string c=oper.top();oper.pop();
	if (c=="and") num.push(x&&y);
	else num.push(x||y);
	return true;
}
bool Not(string s){
	if (s=="true") return 0;
	if (s=="false") return 1;
}
string s;bool flag;
int main(){
	getline(cin,s);
	stringstream ss(s);
	while (ss>>s){
		if (s=="not"){
			if (ss>>s){
				if (s!="true"&&s!="false"){
					printf("error");
					return 0; 
				}
				else num.push(Not(s));
			}
			else{
				printf("error");
				return 0;
			}
			flag=false;
		}
		else if (cti(s)){
			if (num.empty()){
				printf("error");
				return 0;
			}
			while (oper.size()&&cti(oper.top())>=cti(s)){
				if (!calc()){
					printf("error");
					return 0;
				}
			}
			oper.push(s);
			flag=false;
		}
		else{
			if (flag){
				printf("error");
				return 0;
			}
			num.push(!Not(s));
			flag=true;
		}
	}
	while (oper.size()){
		if (!calc()){
			printf("error");
			return 0;
		}
	}
	if (num.size()!=1){
		printf("error");
		return 0;
	}
	if (num.top()) printf("true");
	else printf("false");
	return 0; 
}

可是,这样真的就 AC 了吗?不是,有 1 1 1 个点错了。为什么?

因为数据中有如这样的情况 not not true,这种情况下我们的程序会输出 error,但是它实际上是应该输出 true 的。我们特殊处理即可。

#include 
using namespace std;
stack<bool> num;
stack<string> oper;
int cti(string s){
	if (s=="or") return 1;
	if (s=="and") return 2;
	if (s=="not") return 3;
	return 0;
} 
inline bool calc(){
	register bool x,y;
	if (num.size()){
		y=num.top();num.pop();
	}
	else return false;
	if (num.size()){
		x=num.top();num.pop();
	}
	else return false;
	string c=oper.top();oper.pop();
	if (c=="and") num.push(x&&y);
	else num.push(x||y);
	return true;
}
inline bool Not(string s){
	if (s=="true") return 0;
	else return 1;
}
string s;bool flag,sign;
int main(){
	getline(cin,s);
	stringstream ss(s);
	while (ss>>s){
		if (s=="not"){
			if (ss>>s){
				if (s!="true"&&s!="false"&&s!="not"){
					printf("error");
					return 0;
				}
				if (s=="true"||s=="false")
					num.push(Not(s));
				else{
					sign=false;
					while (ss>>s){
						if (s!="true"&&s!="false"&&s!="not"){
							printf("error");
							return 0; 
						}
						else{
							if (s=="not") sign=!sign;
							else{
								num.push(sign?Not(s):!Not(s));
								break;
							}
						}
					}
				}
			}
			else{
				printf("error");
				return 0;
			}
			flag=false;
		}
		else if (cti(s)){
			if (num.empty()){
				printf("error");
				return 0;
			}
			while (oper.size()&&cti(oper.top())>=cti(s)){
				if (!calc()){
					printf("error");
					return 0;
				}
			}
			oper.push(s);
			flag=false;
		}
		else{
			if (flag){
				printf("error");
				return 0;
			}
			num.push(!Not(s));
			flag=true;
		}
	}
	while (oper.size()){
		if (!calc()){
			printf("error");
			return 0;
		}
	}
	if (num.size()!=1){
		printf("error");
		return 0;
	}
	if (num.top()) printf("true");
	else printf("false");
	return 0; 
}
/*
注意!需要特殊判断如not not true这样的情况,
否则无法通过第七个测试点
*/

这提示我们,只有全面的思考,才能获得高分或者 AC

你可能感兴趣的:(思维题,全面的思考,原创)