3 条题解

  • 2
    @ 2025-4-14 15:46:01

    建议宁老师把题目数量调整至 4~5 题,这样能使得我们更加深入地思考每一道题。

    本题就是,其实注意到一些性质很快就可以做出来,但是大家场上没有充分思考。

    首先注意到每个点的对应唯一,然后我们发现其实就是在序列上选出一些数来看看有没有逆序对。

    然后注意到逆序对最后一个字是对,也就是两个数的关系,所以我们并不在乎给定的字符集整体,而是它们当中的每一对。

    于是我们枚举26*26的字符对,进行预处理,然后暴力判断即可。

    然后我以为本题多测,于是想到了更优秀的做法,建议宁老师加强数据到 1e61e6

    好了我来填坑了,我们前面已经提到,其实就是对于所有的字母对求是否存在逆序对。

    然后我们其实可以同时处理所有的字母对,我们首先考虑,一般来说我们在统计逆序对的时候使用树状数组统计个数,这里我们可以类似去处理。

    这里我不再多写,代码里有注释,自己学习理解一下,因为我不太好用单纯的文字进行描述。

    #include<bits/stdc++.h>
    using namespace std;
    char a[100005],b[100005];
    int id[100005],cntt[100005];
    vector<int>vec1[30],vec2[30];
    int S[26],c[100005],n,m;
    
    //我们其实可用类似的方法来统计一个字母集合。
    //以下的树状数组可以统计每一个数字对应的字母集合。 
    inline int lowbit(int x){return x&-x;}
    inline void add(int x,int S){for(int i=x; i<=m+1; i+=lowbit(i))c[i]|=S;}
    inline int sum(int x){int ans=0;for(int i=x; i; i-=lowbit(i))ans|=c[i];return ans;}
    
    
    int main(){
    	scanf("%s%s",a+1,b+1);
    	n=strlen(a+1),m=strlen(b+1);
    	for(int i=1; i<=n; i++)vec1[a[i]-'a'].push_back(i);
    	for(int i=1; i<=m; i++)vec2[b[i]-'a'].push_back(i);
    	for(int i=0; i<26; i++){
    		if(vec1[i].size()!=vec2[i].size())S[i]|=(1<<i);
    		while(vec1[i].size()>vec2[i].size())vec2[i].push_back(0);
    		for(int j=0; j<vec1[i].size(); j++){
    			int x=vec1[i][j],y=vec2[i][j];
    			id[x]=y;
    		}
    	}for(int i=1; i<=n; i++){
    		S[a[i]-'a']|=sum(m-id[i]);
    		add(m-id[i]+1,(1<<a[i]-'a'));
    		/*但是我们发现,因为树状数组存储的东西必须满足可差分性。而集合的并显然不满足,不过没有关系
    		  我们可以考虑把它倒序存到树状数组里,这样查找的时候就是前多少项的并,然后就不需要可差分性了。 
    		*/
    		
    	}
    	for(int i=0; i<26; i++){
    		for(int j=0; j<26; j++){
    			if((S[i]>>j)&1)S[j]|=(1<<i);
    			/* 我们要把这个邻接矩阵对称一下。*/
    		}
    	}
    	int t;scanf("%d",&t);
    	while(t--){
    		char ch[30];scanf("%s",ch+1);
    		int s=0;
    		int t=strlen(ch+1);
    		for(int i=1; i<=t ;i++)s|=(1<<ch[i]-'a');
    		bool ok=1;
    		for(int i=1; i<=t; i++){
    			if(s&S[ch[i]-'a']){
    				ok=0;
    				break;
    			}
    		}
    		if(ok){
    			putchar('Y');
    		}else{
    			putchar('N');
    		}
    	}
    	return 0;
    } 
    
    ```
    `
    • -2
      @ 2025-4-17 9:02:54

      考虑两个字母是否能够同时出现

      bool check(char x,char y){
      	int cnt = 0;
      	for(char c : a) if(c == x) cnt++;
      	for(char c : b) if(c == x) cnt--;
      	if(cnt != 0) return 0;
      	for(char c : a) if(c == y) cnt++;
      	for(char c : b) if(c == y) cnt--;
      	if(cnt != 0) return 0;
      	int i = 0,j = 0;
      	int n = a.size();
      	int m = b.size();
      	while(i < n && j < m){
      		while(i < n && a[i] != x && a[i] != y) i++;
      		while(j < m && b[j] != x && b[j] != y) j++;
      		if(i < n && j < m && a[i] != b[j]) return 0;
      		i++; j++;
      	}
      	return 1;
      }
      
      

      然后每读入一个s判一下就好

      • -8
        @ 2026-1-7 8:46:18

        对 A、B 进行预处理,尝试寻找形如 “颜色 p 和颜色 q 不能共存” 的限制。

        若颜色 x 在 A、B 中出现次数不一样,则一定不选,记为 f (x,x)=1.

        剩余部分构成了若干组相同颜色匹配,如图:

        容易发现,若 p 和 q 共存,则一定存在 p 的一组匹配与 q 的一组匹配交叉

        故枚举 p、q 的一组匹配(A 中的第 a 个 B 中的第 b 个)。

        记 A 中 a 前面 q 的个数为 c1,B 中 b 前面 q 的个数为 c2,通过二分求出 c1、c2,若 c1≠c2,则 f (p,q)=f (q,p)=1。

        解释:若c1>c2,则A中第c1个q在B中的匹配一定在b右边,故一定交叉。反之亦然。

        对于每一组查询,枚举每一条限制,若都满足则合法,否则不合法。

        时间复杂度分析:枚举所有 p 和 q 的一组匹配,等价于枚举所有匹配,最多为 n。

        这部分时间复杂度 O (sn logn),s 表示字母种类数。

        限制最多 s² 条,查询复杂度 O (s²k) 总复杂度 O (sn logn + s²k),s=26

        别急 后面还有

        #include<bits/stdc++.h>
        using namespace std;
        string s1,s2,s;
        int len1,len2,a[101000],b[101000],cnt1[33],cnt2[33];
        int v1[33][101000],v2[33][101000];
        int unok[33][33],k;//unok即为不可行关系 
        int vis[33];
        signed main(){
        	ios::sync_with_stdio(0);cin.tie(0);
        	cin>>s1>>s2;
        	len1=s1.size();len2=s2.size();
        	s1=" "+s1;s2=" "+s2;
        	for(int i=1;i<=len1;i++){
        		a[i]=s1[i]-'a'+1;
        		v1[a[i]][++cnt1[a[i]]]=i;
        		//A串中颜色a[i]出现第cnt1[a[i]]次是在位置i 
        	}for(int i=1;i<=len2;i++){
        		b[i]=s2[i]-'a'+1;
        		v2[b[i]][++cnt2[b[i]]]=i;
        	}
        	for(int i=1;i<=26;i++){//p
        		if(cnt1[i]!=cnt2[i]) unok[i][i]=1;
        		else{
        			for(int j=1;j<=cnt1[i];j++){//每一组匹配 
        				int xa=v1[i][j],xb=v2[i][j];
        				for(int o=1;o<=26;o++){//q
        					if(o==i||cnt1[o]==0||cnt2[o]==0) continue;
        					//二分 
        					int al=0,ar=cnt1[o];
        					while(al<ar){
        						int mid=(al+ar+1)/2;
        						if(xa<v1[o][mid]) ar=mid-1;
        						else al=mid;
        					}
        					int bl=0,br=cnt2[o];
        					while(bl<br){
        						int mid=(bl+br+1)/2;
        						if(xb<v2[o][mid]) br=mid-1;
        						else bl=mid;
        					}
        					if(bl!=al) unok[i][o]=unok[o][i]=1;
        				}
        			}
        		}
        	}
        	cin>>k;
        	for(int i=1;i<=k;i++){
        		cin>>s;
        		int len=s.size();s=" "+s;
        		memset(vis,0,sizeof(vis));
        		for(int j=1;j<=len;j++)	vis[s[j]-'a'+1]=1;
        		int flag=0;
        		for(int fr=1;fr<=26;fr++){
        			for(int to=1;to<=26;to++){
        				if(unok[fr][to]&&vis[fr]&&vis[to]){
        					flag=1;
        					break;
        				}
        			}if(flag) break;
        		}
        		if(flag) cout<<"N";
        		else cout<<"Y";
        	}
        	return 0;
        }
        

        Update 1.7

        听说有个人要加大数据卡我

        考虑优化:

        注意到二分每个颜色q的时候最前面的部分被考虑了多次,于是考虑同时处理每一个q。

        处理出当前 A 串中 a 前面的每一个颜色的出现次数 cnta 和 B 串中 b 前面每一个颜色的出现次数 cntb。

        若cnta[q]≠cntb[q] 则交叉。

        可达到均摊复杂度 O (sn)。

        查询部分,注意到我们可以把限制的第二维二进制压缩成一个数,复杂度变为 O (sn)。

        总复杂度 O (sn),s=26

        #include<bits/stdc++.h>
        using namespace std;
        //#define int long long
        const int inf=1e15;
        string s1,s2,s;
        int len1,len2;
        int a[1010000],b[1010000];
        int cnt1[33],cnt2[33];
        int v1[27][1010000],v2[27][1010000];
        int unok[33],k;//unok即为不可行关系 
        int vis;
        int cnta[33],cntb[33];
        signed main(){
        	ios::sync_with_stdio(0);
        	cin.tie(0);
        	cin>>s1>>s2;
        	len1=s1.size();
        	len2=s2.size();
        	s1=" "+s1;
        	s2=" "+s2;
        	for(int i=1;i<=len1;i++){
        		a[i]=s1[i]-'a'+1;
        		v1[a[i]][++cnt1[a[i]]]=i;
        		//A串中颜色a[i]出现第cnt1[a[i]]次是在位置i 
        	}for(int i=1;i<=len2;i++){
        		b[i]=s2[i]-'a'+1;
        		v2[b[i]][++cnt2[b[i]]]=i;
        	}
        	for(int i=1;i<=26;i++){//p
        		if(cnt1[i]!=cnt2[i]) unok[i]=(unok[i]|(1<<i));
        		else{
        			memset(cnta,0,sizeof(cnta));
        			memset(cntb,0,sizeof(cntb));
        			for(int j=1;j<=cnt1[i];j++){//每一组匹配 
        				int xa=v1[i][j],xb=v2[i][j];
        				for(int x=v1[i][j-1]+1;x<=xa;x++) cnta[a[x]]++;
        				for(int x=v2[i][j-1]+1;x<=xb;x++) cntb[b[x]]++;
        				for(int o=1;o<=26;o++){//q
        					if(o==i||cnt1[o]==0||cnt2[o]==0) continue;
        					if(cnta[o]!=cntb[o]){
        //						unok[i][o]=unok[o][i]=1;
        						unok[i]=(unok[i]|(1<<o));
        						unok[o]=(unok[o]|(1<<i));
        					}
        				}
        			}
        		}
        	}
        	cin>>k;
        	for(int i=1;i<=k;i++){
        		cin>>s;
        		int len=s.size();
        		s=" "+s;
        		vis=0;
        		for(int j=1;j<=len;j++){
        			vis=(vis|(1<<(s[j]-'a'+1)));
        		}
        		int flag=0;
        		for(int x=1;x<=26;x++){
        			if(vis&(1<<x))flag=(flag|(vis&unok[x]));
        			if(flag) break;
        		}
        		if(flag) cout<<"N";
        		else cout<<"Y";
        	}
        	return 0;
        }
        

        Edited by ZT

        • 1

        信息

        ID
        163
        时间
        1000ms
        内存
        256MiB
        难度
        8
        标签
        (无)
        递交数
        41
        已通过
        7
        上传者