0924 模拟赛

写在前面​

\(cb\)什么都不会\(QAQ\)bp.gif

T1 括号序列

链接

Idea

题目要求为子串,即连续。

即,当前面出现一个合法的子串,后面又有一个合法的,\(Ans=1+1\);

同理,如果前面有\(n\)个合法的,后面又有一个合法的,\(Ans=1+n\)

于是每当遇到一个合法的括号对儿时,看它前面是否有连续的括号对儿

\(stack\)记录,用\(stack.tmp\)来记录当前括号作为两个连续合法序列是的分隔时,当前括号后面有多少合法序列。cy.png

某机房\(dalao\)说他就写了个爆搜,然后....他\(A\)了....ts.png

还有个\(dalao\)写了个双端队列,然后....他\(MLE\)了....ts.png

Code

struct Node{
   int x,tmp;	
}b;
char a[maxn];
stack<Node>s;
signed main(){
//	freopen(File".in","r",stdin);
//	freopen(File".out","w",stdout);
   cin>>a+1;
   int len=strlen(a+1);
   int ans=0,sum=0;
   for(int i=1;i<=len;i++){
   	if(a[i]=='(') b.x=1;
   	else b.x=2;
   	if(s.size()){
   		if(s.top().x==1&&b.x==2){
   			s.pop();
   			if(s.empty()){
   				ans+=(sum+1);
   				sum++;
   			}
   			else{
   				ans+=(s.top().tmp+1);
   				s.top().tmp++;
   			}	 
   		}
   		else s.push(b);
   	}
   	else s.push(b);
   }
   printf("%lld",ans);
   return 0;
}

T2 和数检测

链接

Idea

这道题...,看完题我就用了\(map\)...

不想再多说些什么了... 代码一看就懂...ts.pngwn.png

只有\(55pts\)....jwdys.jpg

\(dalao\)们采取了树状数组双指针二分等操作写了这道题。果然,我就是个\(cb\)youl.png

Code

int a[maxn];
signed main(){
//	freopen(File".in","r",stdin);
//	freopen(File".out","w",stdout);
	int T=read();
	while(T--){
		map<int,int>p;
		int flag=1;
		int n=read(),m=read();
		for(int i=0;i<n;i++){
			a[i]=read(); 
			if(flag){
				if(p[m-a[i]]==1) flag=0;
				if(p[m-a[i]]!=1){
					p[a[i]]=1;
					p[m-a[i]]=2;
				}
				if(p[a[i]]==2) flag=0;
			}
		} 
		if(!flag) puts("1");
		else puts("0");
	}
	return 0;
} 

以下正解

const int maxx=10000010;
int n,flag,m;
int a[maxn],f[maxx];
int main(){
//	freopen(File".in","r",stdin);
//	freopen(File".out","w",stdout);
	int T=read();
	for(int k=1;k<=T;k++){
		if(k!=1)for(int i=1;i<=n;i++)if(a[i]<=maxx-10)f[a[i]]=0;
		n=read();m=read();flag=0;
		for(int i=1;i<=n;i++){
			a[i]=read();
			if(a[i]<=maxx)f[a[i]]=1;
		}
		if(m<=10000000){
			for(int i=1;i<=n;i++){
				if(a[i]>m)continue;
				if(f[m-a[i]]){flag=1;break;}
			}
			if(flag)puts("1");
			else puts("0");
		}
		else{
			sort(a+1,a+1+n);
			int j=n,w;
			for(int i=1;i<=n;++i){
				if(a[i]>m)break;
				w=m-a[i];
				while(a[j]>w&&j) j--;
				if(!j)break;
				if(a[j]==w){flag=1;break;}
			}
			if(flag)puts("1");
			else puts("0");
		}
	}
	return 0;
}

T3 与

链接

Idea

考场上,我一直在琢磨样例一咋搞,结果还是没搞出来。我太菜了\(QAQ\)lb.png

到最后还是没搞出来,jwdys.jpg

看看题解的说法

考虑容斥,则\(Ans= \displaystyle \sum_{c=0}^{131071} (-1)^c \times f(c)\)

其中,变量\(c\)枚举的是子集,代表需要限制为不符合要求的二进制位(即这些位一定得一边是0一边是1)。\(f(c)\)指在\(c\)的限制下的方案数。

由于有一些位一定得一边是0一边是1,所以这些位为1的元素一定得归一边,可以考虑把它们合并为一个元素。如果有多个位有限制,那就用并查集来合并元素。合并后,剩余的这些元素可以被任意分到左侧或右侧,则\(f(c) = 2^k − 2\),其中\(k\)表示合并后的元素个数,减去2是为了避免有一侧没有分配到元素。具体的\(-2\)是因为你在枚举的时候并不能全部枚举完,要删除两种情况,就是枚举所有的枚举0种的情况

如果\(n\)个数的某个二进制位全部为0或为1,则可以直接把这一位去掉。这样可以避免在后续计算时的特殊讨论。

Code​

int n,ans;
int a[maxn],b[maxn],fa[maxn];
inline int find(int x){
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}
signed main(){
//	freopen(File".in","r",stdin);
//	freopen(File".out","w",stdout); 
	n=read(); 	
	for(int i=1;i<=n;i++) a[i]=read();
	b[0]=1; 
	for(int i=1;i<=maxn;i++) b[i]=b[i-1]*2;
	for(int d=0;d<=131071;d++){
		int d1=d,cnt=1,s;//容斥 
		for(int i=1;i<=n;i++) fa[i]=i;
		for(int i=0;i<17;i++,(d1>>=1),s=i)//枚举d的位置 
		if(d1&1){
			cnt=-cnt;//容斥 
			int j1=b[i],j2=0,flag=0;//j1的初始值就是i满位的时候
			for(int j=1;j<=n;j++)//枚举所有的数
			if(!(a[j]&j1)){//如果这个数没有超过满位的j1
				flag=1;//标记一下,如果是有的数大于现在的限制了,就直接扩大限制而不必是仅仅跳出次循环了
				if(!j2) j2=find(j);//向上找到他的父亲节点,就是在比他位数少的数中有1的
				else fa[find(j)]=j2;//要是之前都没有就把他直接赋成根节点,这样就会得到好多集合 
			}
			if(!flag) break;
		}
		if(s!=17) continue;
		int num=0;
		for(int i=1;i<=n;i++) if(i==find(i)) num++;//记录集合个数
		ans+=cnt*(b[num]-2);
	}
	printf("%lld",ans);
	return 0;
}

\[The\quad End \]

\[\texttt{多幸运,爱你这件事情;成为我,今生最对的决定-《多幸运》} \]

posted @ 2019-09-28 07:58  云山乱  阅读(178)  评论(0编辑  收藏  举报