【学习笔记】CF1290

出这种猜结论题,我挺无语的

不妨从字符集大小为 2 2 2入手。如果 s 1 ≠ s n s_1\ne s_n s1=sn那么显然将 s 1 s_1 s1 s n s_n sn交换就能构造出合法的方案。

最难的地方可能就在于发现这个条件事实上也是充要的。道理很简单,画一个折线图不难发现中间必定有交。

然后是字符集 ≥ 3 \ge 3 3的情况。我们发现这种情况直接把三种字符顺次拼接起来就做完了。

不过这道题的二级结论居然要用到 s 1 = s n s_1=s_n s1=sn这个小条件,倒挺令我意外的。

#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; string s; int n,Q,sum[200005]; vector<int>vec[26]; int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>s,n=s.size(); for(int i=0;i<n;i++){ vec[s[i]-'a'].pb(i+1); } for(int i=2;i<=n;i++){ sum[i]=sum[i-1]+(s[i-1]==s[i-2]); } cin>>Q; while(Q--){ int l,r; cin>>l>>r; if(l==r){ cout<<"Yes"<<"\n"; } else if(sum[r]-sum[l]==r-l){ cout<<"No"<<"\n"; } else if(s[l-1]!=s[r-1]){ cout<<"Yes"<<"\n"; } else{ int tot=0; for(int i=0;i<26;i++){ int it=lower_bound(vec[i].begin(),vec[i].end(),l)-vec[i].begin(); if(it!=vec[i].size()&&vec[i][it]<=r){ tot++; } } if(tot>=3){ cout<<"Yes"<<"\n"; } else{ cout<<"No"<<"\n"; } } } }

近期做过最难的交互题了。

逻辑链条有点长。但是我就是卡在第一步 看来大脑该升级了

对于每个元素,我们希望求出它之前是否出现过。将 k 2 \frac{k}{2} 2k个元素分成一组,按顺序加入两组元素可以检测后一组的元素是否在前一组出现过。同时,同一组内的相同元素我们也考虑到了。这样一共有 ( 2 n k 2 ) = 2 n 2 k 2 \binom{\frac{2n}{k}}{2}=\frac{2n^2}{k^2} (2k2n)=k22n2对二元组,每次检验需要询问 k k k次,总操作次数 2 n 2 k \frac{2n^2}{k} k2n2

不难发现上述做法的瓶颈在于,每次遍历两个块的时候都要清空队列,效率太低。我们先给出,对于一个 n n n个点( n n n是偶数)的完全图有一个性质,就是覆盖它的边集最少要用的链的数目是
n 2 \frac{n}{2} 2n,具体构造是我们枚举 s ∈ [ 1 , n 2 ] s\in [1,\frac{n}{2}] s[1,2n],然后按照 s → s − 1 → s + 1 → s − 2 → s + 2 s\to s-1\to s+1\to s-2\to s+2 ss1s+1s2s+2这样的顺序遍历,这样每条边恰好被覆盖一次。这样总操作次数 k 2 ⋅ ( 2 n k 2 ) = n 2 k \frac{k}{2}\cdot \binom{\frac{2n}{k}}{2}=\frac{n^2}{k} 2k(2k2n)=kn2,可以通过。

这道题最难的部分在于,如何处理 i < j i<j i<j的限制。我们考虑维护一个集合表示目前没有值与其重复的元素,对于每个 a i a_i ai,如果发现集合中有一个数值与其相同,那么就把 a i a_i ai删去。那么我们只将在集合内的数加入队列中,这样如果查询到有重复的元素,说明这个元素的值还没有被删完,就可以放心把这个元素给删去了。总而言之,相同值的元素最终一定只会保留一个。

remark : \text{remark}: remark: 确实是非常聪明的解法呀。

#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; int n,K,siz,cnt,is[2000]; int qry(int x){ cout<<"?"<<" "<<x<<endl; char c;cin>>c;return (c=='Y'); } void solve(int x){ for(int i=(x-1)*siz+1;i<=x*siz;i++){ if(is[i]&&qry(i)){ is[i]=0; } } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>K;siz=max(K/2,1),cnt=n/siz; for(int i=1;i<=n;i++)is[i]=1; for(int i=1;i<=cnt/2;i++){ int cur=-1; for(int j=1;j<=cnt;j++){ solve((i+j/2*cur+cnt-1)%cnt+1),cur*=-1; } cout<<"R"<<endl; } int res=0; for(int i=1;i<=n;i++)res+=is[i]; cout<<"!"<<" "<<res<<endl; }

数据结构题。不会。所以跳了。

要做这道题,首先必须要知道一个结论:一旦组成凸包的向量的集合确定,这个凸包的形态就是唯一确定的。这是因为,不存在两条向量是平行的。于是问题就转化成了单纯的计数问题。

这道题最难的部分在于,如何优化状态。比如,记 A = ∑ x i > 0 a i x i , B = ∑ x i < 0 a i x i , C = ∑ y i > 0 a i y i , D = ∑ y i < 0 a i y i A=\sum_{x_i>0}a_ix_i,B=\sum_{x_i<0}a_ix_i,C=\sum_{y_i>0}a_iy_i,D=\sum_{y_i<0}a_iy_i A=xi>0aixi,B=xi<0aixi,C=yi>0aiyi,D=yi<0aiyi,那么我们有 A = B ≤ m , C = D ≤ m A=B\le m,C=D\le m A=Bm,C=Dm

然后我们按二进制位数 从低位到高位 来确定。更确切的说,这是一个更类似于倍增的做法,即从在模 2 k 2^k 2k意义下拓展到模 2 k + 1 2^{k+1} 2k+1的意义,直到这个模取得足够大的时候,我们就得到了确切的限制。当然,我们同样要记录 A / 2 k A/2^k A/2k的值,以便求出 A   m o d   2 k + 1 A\bmod 2^{k+1} Amod2k+1的答案。这其实非常好估计,因为 a i ∈ [ 0 , 2 k ) a_i\in [0,2^k) ai[0,2k),所以 A / 2 k ≤ ∑ x i > 0 x i ≤ 20 A/2^{k}\le \sum_{x_i>0}x_i\le 20 A/2kxi>0xi20

至于 A ≤ m A\le m Am这个限制,用 0 / 1 0/1 0/1状态记录是否超出即可。

于是只剩下代码难度。复杂度 O ( 2 0 4 2 5 log ⁡ M ) O(20^42^5\log M) O(20425logM)

#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int mod=998244353; int n,m,a[35],len; ll dp[35][25][25][25][25][2][2],X[5],Y[5]; void add(ll &x,ll y){ x=(x+y)%mod; } ll dfs(int x,int A,int B,int C,int D,int f1,int f2){ if(x>len){ return !(A||B||C||D||f1||f2); } if(~dp[x][A][B][C][D][f1][f2])return dp[x][A][B][C][D][f1][f2]; ll res(0); for(int i=0;i<1<<n;i++){ int A2=A,B2=B,C2=C,D2=D; for(int j=0;j<n;j++){ if(i>>j&1){ if(X[j]>0)A2+=X[j]; else if(X[j]<0)B2-=X[j]; if(Y[j]>0)C2+=Y[j]; else if(Y[j]<0)D2-=Y[j]; } } if(A2%2==B2%2&&C2%2==D2%2){ add(res,dfs(x+1,A2/2,B2/2,C2/2,D2/2,!(A2%2<a[x]||A2%2==a[x]&&!f1),!(C2%2<a[x]||C2%2==a[x]&&!f2))); } } return dp[x][A][B][C][D][f1][f2]=res; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m;for(int i=0;i<n;i++)cin>>X[i]>>Y[i]; memset(dp,-1,sizeof dp); while(m)a[++len]=m&1,m>>=1; cout<<(dfs(1,0,0,0,0,0,0)+mod-1)%mod; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530003.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-03-30 【题解】[SHOI2011] 银行家
点击右上角即可分享
微信分享提示