为了监督自己补题,顺便存代码,把码的题扔在Blog上吧。
Educational Codeforces Round 82 (Rated for Div. 2) A Erasing Zeroes 题解 把夹在中间的都删了就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int t; cin >>t; while (t--){ string st; cin >>st; int len=st.size(); int left=-1 ,right; int ans=0 ; for (int i=0 ;i<len;i++){ if (st[i]=='1' ){ left=i; break ; } } if (left==-1 ) cout <<0 <<endl ; else { for (int i=len-1 ;i>=0 ;i--){ if (st[i]=='1' ){ right=i; break ; } } for (int i=left;i<=right;i++){ if (st[i]=='0' ) ans++; } cout <<ans<<'\n' ; } } }
B National Project 题解 如果g≥b,显然前n天一定满足条件。
否则,每需要g天,就需要一段整个(g+b)天来提供。这里特判一下,如果所需要的好天数是g的倍数,那么不用再等最后的b天了。这里有个问题是,这样算的总天数可能比n小,可以特判,也可以直接和n取max。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ ll n,g,b; cin >>n>>g>>b; if (g>=b) cout <<n<<endl ; else { long long half=(n+1 )/2 ; long long t=half/g; t*=g+b; if (half<=g) cout <<n<<endl ; else if (half%g==0 ) cout <<max(n,t-b)<<endl ; else cout <<max(n,t+half%g)<<endl ; } } }
C Perfect Keyboard 题解 按照相邻关系建图,只要图只包含孤立节点和链就可以成立。之所以只能是链,因为移动方法使得每个字母至多和两个其他字母相邻,而作为一个串不能首尾相连,所以环也不可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <bits/stdc++.h> #define ll long long using namespace std ;int mmp[30 ][5 ];bool vis[30 ];string ans;bool connect (int x,int y) { if (mmp[x][0 ]==2 ){ if (y!=mmp[x][1 ]&&y!=mmp[x][2 ]) return 0 ; } else if (mmp[x][0 ]==1 ){ if (y!=mmp[x][1 ]){ mmp[x][0 ]=2 ; mmp[x][2 ]=y; } } else { mmp[x][0 ]=1 ; mmp[x][1 ]=y; } return 1 ; } void dfs (int x) { vis[x]=1 ; ans+='a' +x; if (!vis[mmp[x][1 ]]) dfs(mmp[x][1 ]); else if (mmp[x][0 ]==2 &&!vis[mmp[x][2 ]]) dfs(mmp[x][2 ]); } int main () { int T; cin >>T; while (T--){ memset (mmp,0 ,sizeof (mmp)); string pd; cin >>pd; int len=pd.size(); bool flg=0 ; for (int i=1 ;i<len;i++){ if (!connect(pd[i-1 ]-'a' ,pd[i]-'a' )||!connect(pd[i]-'a' ,pd[i-1 ]-'a' )){ cout <<"NO\n" ; flg=1 ; break ; } } if (flg) continue ; memset (vis,0 ,sizeof (vis)); ans="" ; for (int i=0 ;i<26 ;i++){ if (!vis[i]){ if (mmp[i][0 ]==0 ){ ans+='a' +i; vis[i]=1 ; } else if (mmp[i][0 ]==1 ){ dfs(i); } } } if (ans.size()==26 ) cout <<"YES\n" <<ans<<'\n' ; else cout <<"NO\n" ; } }
D Fill The Bag 题解 在记录二进制下每一位有几个数,然后从低位到高位减一遍,答案是类似“往上借一位”这个操作的次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <bits/stdc++.h> #define ll long long using namespace std ;int bits[100 ];int main () { int t; cin >>t; while (t--){ ll n; int m; cin >>n>>m; memset (bits,0 ,sizeof (bits)); int a; ll tota=0 ; for (int i=0 ;i<m;i++){ cin >>a; tota+=a; bits[int (log2(a))]++; } if (tota<n){ cout <<"-1\n" ; continue ; } int pos=0 ; int op=0 ; while (n!=0 ){ if (n%2 ){ if (!bits[pos]){ int tpos=pos; while (!bits[tpos]){ bits[tpos]=1 ; tpos++; } bits[tpos]--; op+=tpos-pos; } bits[pos]--; } n>>=1 ; bits[pos+1 ]+=bits[pos]/2 ; pos++; } cout <<op<<'\n' ; } }
Codeforces Round #617 (Div. 3) A Array with Odd Sum 题解 偶数是没有用的,所以只要看奇数能不能有奇数个就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int n; cin >>n; int a_odd=0 ,a_even=0 ; for (int i=0 ;i<n;i++){ int tmp; cin >>tmp; if (tmp%2 ) a_odd++; else a_even++; } if (a_odd==0 ){ cout <<"NO\n" ; } else if (a_even==0 ){ if (a_odd%2 ){ cout <<"YES\n" ; } else { cout <<"NO\n" ; } } else cout <<"YES\n" ; } }
B Food Buying 题解 十个十个买就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int t; cin >>t; while (t--){ int s; cin >>s; int ans=0 ; while (s>=10 ){ ans+=s-s%10 ; s=s/10 +s%10 ; } cout <<ans+s<<'\n' ; } }
C Yet Another Walking Robot 题解 用map记录上一次某位置出现的时间,因为要求最小的一段,所以只需要保存上一次的时间而非所有历史时间,然后每次有重复就更新时间和答案就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <bits/stdc++.h> using namespace std ;map <pair<int ,int >,int > mmp;map <pair<int ,int >,int >::iterator iter;int main () { int t; cin >>t; while (t--){ int n; cin >>n; string op; cin >>op; mmp.clear(); mmp.insert(map <pair<int ,int >,int >::value_type(make_pair(0 ,0 ),0 )); int x=0 ,y=0 ; int ll=0 ,rr=n*2 ; for (int i=1 ;i<=n;i++){ char ch=op[i-1 ]; if (ch=='L' ){ x--; } else if (ch=='R' ){ x++; } else if (ch=='U' ){ y++; } else { y--; } iter=mmp.find(make_pair(x,y)); if (iter!=mmp.end()){ if (i-iter->second<=rr-ll+1 ){ ll=iter->second+1 ; rr=i; } iter->second=i; }else { mmp.insert(map <pair<int ,int >,int >::value_type(make_pair(x,y),i)); } } if (ll) cout <<ll<<' ' <<rr<<'\n' ; else cout <<"-1\n" ; } }
D Fight with Monsters 题解 预处理出每个怪物需要用几次黑科技才能让我得分,然后从小到大贪心取就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int n,a,b,k; cin >>n>>a>>b>>k; int h[200005 ]; for (int i=0 ;i<n;i++){ int tmp; cin >>tmp; tmp%=a+b; if (!tmp) tmp=a+b; h[i]=(tmp+(a-1 ))/a-1 ; } sort(h,h+n); int ans=0 ,i=0 ; while (i<n&&k>=0 ){ k-=h[i]; if (k>=0 ) ans++; i++; } cout <<ans<<endl ; }
E1 String Coloring (easy version) 题解 从左到右处理,保证每一位及其之前全部有序再进行下一位的交换处理,那么每个数字只需要和它前面比它大的交换(因为小于等于它的已经到了它该到的位置了)。问题就变成了求解单调不下降子序列数量,如果序列小于等于2个就有解,按照所在序列分配颜色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int n; cin >>n; string st; cin >>st; int s0=0 ,s1=0 ; int c[233 ]; bool flg=1 ; for (int i=0 ;i<n;i++){ if (st[i]-'a' +1 >=s0){ c[i]=0 ; s0=st[i]-'a' +1 ; } else if (st[i]-'a' +1 >=s1){ c[i]=1 ; s1=st[i]-'a' +1 ; } else { flg=0 ; cout <<"NO\n" ; break ; } } if (flg){ cout <<"YES\n" ; for (int i=0 ;i<n;i++) cout <<c[i]; } }
E2 String Coloring (hard version) 传送门 题解 和前面一样,不过这次改成多个序列了。其实可以加优化,但是数据范围小,至多就26个序列,所以用穷举检测就过得去了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int n; cin >>n; string st; cin >>st; int c[200005 ]; int seq[30 ]; memset (seq,0 ,sizeof (seq)); for (int i=0 ;i<n;i++){ for (int j=1 ;j<30 ;j++){ if (seq[j]<=st[i]-'a' +1 ){ c[i]=j; seq[j]=st[i]-'a' +1 ; break ; } } } int res=1 ; while (seq[res]!=0 ){ res++; } cout <<res-1 <<'\n' ; for (int i=0 ;i<n;i++){ cout <<c[i]<<' ' ; } }
Codeforces Round #616 (Div. 2) A Even But Not Even 题解 根据奇偶性,显然当且仅当存在至少两个奇数位的时候有解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int len; cin >>len; string st; cin >>st; char ch=' ' ; bool flg=1 ; for (int i=0 ;i<len;i++){ if ((st[i]-'0' )%2 ){ if (ch!=' ' ){ cout <<ch<<st[i]<<'\n' ; flg=0 ; break ; } else { ch=st[i]; } } } if (flg) cout <<"-1\n" ; } }
B Array Sharpening 题解 因为可以无限次使用-1-1-1-1-1(太暴力了),所以可以直接减到最小,即第零位减到0,第一位减到1……。然后再从后往前减一遍,最后寻找一下是否中间有地方能接上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> #define ll long long using namespace std ;int a[300005 ];int main () { int T; cin >>T; while (T--){ int n; cin >>n; int linc=0 ; bool flg=1 ; for (int i=0 ;i<n;i++){ cin >>a[i]; if (flg&&a[i]>=i) linc++; else flg=0 ; } int ldec=0 ; flg=1 ; for (int i=n-1 ;i>=0 ;i--){ if (flg&&a[i]>=n-1 -i) ldec++; else flg=0 ; } if (linc+ldec>n) cout <<"Yes\n" ; else cout <<"No\n" ; } }
C Mind Control 题解 首先如果可控制的人数k超过m-1,那肯定后面的是没意义的。其次先手控制和后手控制造成的局面是一样的,所以不妨就控制前k个人。那么对应的,有m-1-k个人无法控制。以及在最后的时候,我自己可以选择两端中较大的,这个也可以控制。
所以,我们枚举可控制的人在前端和后端分布的情况,再枚举不可控制的人的分布,取最劣解,最后取其中的最优解就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> #define ll long long using namespace std ;int a[5003 ];int main () { int t; cin >>t; while (t--){ int n,m,k; cin >>n>>m>>k; for (int i=0 ;i<n;i++){ cin >>a[i]; } k=min(m-1 ,k); int ans=-1 ; for (int i=0 ;i<=k;i++){ int tmp=1e9 +9 ; for (int j=0 ;j<=m-1 -k;j++){ tmp=min(tmp,max(a[i+j],a[n-(m-i-j)])); } ans=max(ans,tmp); } cout <<ans<<'\n' ; } }
D Irreducible Anagrams 题解 对于字串来说,其存在符合题意的对应串的情况为——
只存在一个字母且长度为1;
只存在两个字母且首尾字母不同;
存在三个及以上字母
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <bits/stdc++.h> #define ll long long using namespace std ;string st;vector <vector <int > > sig;bool check (int l,int r) { int diff=0 ; for (int i=0 ;i<26 ;i++){ diff+=sig[l-1 ][i]!=sig[r][i]; } if (diff==1 ) return l==r; else if (diff==2 ) return st[l-1 ]!=st[r-1 ]; else return 1 ; } int main () { sig.clear(); vector <int > sig0(26 ,0 ); sig.push_back(sig0); cin >>st; int len=st.size(); for (int i=0 ;i<len;i++){ sig.push_back(sig[i]); sig[i+1 ][st[i]-'a' ]++; } int q; cin >>q; while (q--){ int l,r; cin >>l>>r; if (check(l,r)) cout <<"YES\n" ; else cout <<"NO\n" ; } }
Educational Codeforces Round 81 (Rated for Div. 2) A Display The Number 题解 一个整数显然位数越多,本身就越大,换言之,要优先拼出来“1”来获得位数。如果线段是奇数的话,我们把第一位变成“7”就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int t; cin >>t; while (t--){ int n; cin >>n; if (n%2 ){ cout <<7 ; n-=3 ; } n/=2 ; for (int i=0 ;i<n;i++) cout <<1 ; cout <<'\n' ; } }
B Infinite Prefixes 题解 对于每一位来说,要么单调增长或下降,要么保持不变,其取决于整个循环节的blance。因此统计一下循环节的blance然后简单if判断就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int n,x; cin >>n>>x; string s; cin >>s; int len=s.size(); int tot_cnt=0 ; for (int i=0 ;i<len;i++){ if (s[i]=='0' ) tot_cnt++; else tot_cnt--; } int now_cnt=0 ; int ans=0 ; if (x==0 ) ans++; for (int i=0 ;i<len;i++){ if (s[i]=='0' ) now_cnt++; else now_cnt--; if (now_cnt==x&&tot_cnt==0 ){ ans=-1 ; break ; } if (now_cnt==x) ans++; if (now_cnt<x&&tot_cnt>0 &&(x-now_cnt)%tot_cnt==0 ) ans++; if (now_cnt>x&&tot_cnt<0 &&(now_cnt-x)%(0 -tot_cnt)==0 ) ans++; } cout <<ans<<'\n' ; } }
C Obtain The String 题解 记录下来每一位后面第一次出现的各个字母的位置,然后照着t跑一边就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <bits/stdc++.h> #define ll long long using namespace std ;int sloc[100005 ][30 ];int main () { int T; cin >>T; while (T--){ string s,t; cin >>s>>t; int ls=s.size(),lt=t.size(); for (int i=0 ;i<ls;i++){ for (int j=0 ;j<26 ;j++){ sloc[i][j]=-1 ; } } for (int i=ls-2 ;i>=0 ;i--){ for (int j=0 ;j<26 ;j++){ if (s[i+1 ]-'a' ==j){ sloc[i][j]=i+1 ; } else { sloc[i][j]=sloc[i+1 ][j]; } } } int ans=1 ,p; if (s[0 ]==t[0 ]){ p=0 ; } else if (sloc[0 ][t[0 ]-'a' ]!=-1 ){ p=sloc[0 ][t[0 ]-'a' ]; } else { cout <<-1 <<'\n' ; continue ; } for (int i=1 ;i<lt;i++){ p=sloc[p][t[i]-'a' ]; if (p==-1 ){ if (s[0 ]==t[i]){ p=0 ; ans++; } else if (sloc[0 ][t[i]-'a' ]!=-1 ){ p=sloc[0 ][t[i]-'a' ]; ans++; } else { ans=-1 ; break ; } } } cout <<ans<<'\n' ; } }
D Same GCDs 题解 因为gcd(a, m) = gcd(a + x, m) = gcd((a+x) % m, m),
而(a+x) % m $\in$ [0, m - 1],
所以即求[0, m / gcd(a, m) )中有多少与m / gcd(a, m) 互质的数,即phi(m / gcd(a, m))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <bits/stdc++.h> #define ll long long using namespace std ;ll gcd (ll a,ll b) { if (!b) return a; gcd(b,a%b); } ll phi (ll n) { ll m=(ll)sqrt (n+0.5 ); ll ans=n; for (ll i=2 ;i<=m;i++){ if (n%i==0 ){ ans=ans/i*(i-1 ); while (n%i==0 ) n/=i; } } if (n>1 ) ans=ans/n*(n-1 ); return ans; } int main () { int T; cin >>T; while (T--){ ll a,m; cin >>a>>m; cout <<phi(m/gcd(a,m))<<'\n' ; } }
Codeforces Round #615 (Div. 3) A Collecting Coins 题解 加起来除以三比较一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int a,b,c,n; cin >>a>>b>>c>>n; int m=(a+b+c+n)/3 ; if (m*3 !=n+a+b+c||m<a||m<b||m<c){ cout <<"NO\n" ; } else { cout <<"YES\n" ; } } }
B Collecting Packages 题解 优先向右,判一下能不能走就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <bits/stdc++.h> #define ll long long using namespace std ;struct P { int x,y; }p[1003 ]; bool cmp_p (P a,P b) { if (a.y==b.y) return a.x<b.x; return a.y<b.y; } int main () { int T; cin >>T; while (T--){ int n; cin >>n; for (int i=0 ;i<n;i++){ cin >>p[i].x>>p[i].y; } p[n].x=0 ;p[n].y=0 ; sort(p,p+n+1 ,cmp_p); bool flg=0 ; for (int i=0 ;i<n;i++){ if (p[i].x>p[i+1 ].x){ flg=1 ; break ; } } if (flg) cout <<"NO\n" ; else { cout <<"YES\n" ; for (int i=0 ;i<n;i++){ for (int j=p[i].x;j<p[i+1 ].x;j++){ cout <<"R" ; } for (int j=p[i].y;j<p[i+1 ].y;j++){ cout <<"U" ; } } cout <<"\n" ; } } }
C Product of Three Numbers 题解 随便找个筛法暴力一遍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <iostream> #include <algorithm> #include <cstring> using namespace std ;const int MAXN=100005 ;int prime[MAXN],prime_tot=0 ;bool check[MAXN];void prime_get () { memset (check,0 ,sizeof (check)); check[0 ]=check[1 ]=1 ; int i,j; for (i=2 ;i<=MAXN;i++) { if (check[i]==0 ) prime[prime_tot++]=i; for (j=0 ;j<prime_tot&&i*prime[j]<=MAXN;j++) { check[i*prime[j]]=1 ; if (i%prime[j]==0 ) break ; } } } int main () { prime_get(); int T; cin >>T; while (T--){ int n; cin >>n; int a=0 ,b=0 ; for (int i=0 ;i<prime_tot;i++){ if (n%prime[i]==0 ){ a=prime[i]; n/=a; break ; } } if (a==0 ){ cout <<"NO\n" ; continue ; } for (int i=0 ;i<prime_tot;i++){ if (n%prime[i]==0 &&){ b=prime[i]; n/=b; break ; } } if (b==0 ||n==1 ){ cout <<"NO\n" ; continue ; } cout <<a<<' ' <<b<<' ' <<n<<endl ; } return 0 ; }
D MEX maximizing 题解 因为可以任意 +x 或 -x ,因此有且只有在模 x 下同余的数字可以相互转化,问题就变成了以模 x 划分的集合的元素数量的维护。看一下最小值就行。这里有个注意点,最小值如果存在多个,只有第一个有用,因为在此之前的集合元素数量都至少比最小值大 1 才能保证能轮到它。
然后全部读进来反过来处理可以保证最小值的单调性,每次是维护是O(1)的,正着做我感觉要线段树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> #define ll long long #define MAXN 400005 using namespace std ;int mx[MAXN],y[MAXN],ans[MAXN];int main () { int q,x; cin >>q>>x; memset (mx,0 ,sizeof (mx)); for (int i=0 ;i<q;i++){ cin >>y[i]; mx[y[i]%x]++; } int minn_num=MAXN; int minn_pos=MAXN; for (int i=0 ;i<x;i++){ if (mx[i]<minn_num){ minn_num=mx[i]; minn_pos=i; } } for (int i=q-1 ;i>=0 ;i--){ ans[i]=minn_num*x+minn_pos; mx[y[i]%x]--; if ((mx[y[i]%x]<minn_num)||(mx[y[i]%x]==minn_num&&(y[i]%x)<minn_pos)){ minn_num=mx[y[i]%x]; minn_pos=y[i]%x; } } for (int i=0 ;i<q;i++) cout <<ans[i]<<endl ; }
E Obtain a Permutation 题解 每一列是独立的所以分开来处理,对于每一列,对每一行的元素判断是否可以移动到正确的位置,如果可以就记载需要移动几次,最后对所有不同的移动次数去一个min。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> #define ll long long #define MAXN 200005 using namespace std ;int move030[200005 ];int main () { int n,m; cin >>n>>m; vector <vector <int > > a(n+5 ,vector <int >(m+5 )); for (int i=0 ;i<n;i++){ for (int j=0 ;j<m;j++){ cin >>a[i][j]; } } ll ans=0 ; for (int j=0 ;j<m;j++){ for (int i=0 ;i<n;i++) move030[i]=0 ; for (int i=0 ;i<n;i++){ if (j+1 <=a[i][j]&&a[i][j]<=n*m-m+j+1 &&(a[i][j]-(j+1 ))%m==0 ){ int tar=(a[i][j]-(j+1 ))/m,ori=i; if (tar<=ori) move030[ori-tar]++; else move030[n-(tar-ori)]++; } } int min_move=MAXN; for (int i=0 ;i<n;i++){ min_move=min(min_move,n-move030[i]+i); } ans=ans+(ll)min_move; } cout <<ans<<"\n" ; }
F Three Paths on a Tree 题解 想了很久怎么写比较优美,最后还是觉得写三个dfs方便。
题目很简单,就是找一下最长链然后在上面找一个最长支链。然后为了写的好看,再找出最长链的一端之后,在找另外一个端点的同时记录下来各点到此端点的距离。接着从另外一个端点反向搜索的时候,只要拿每个点到两端的距离和减去最长链的长度再除以二就是这个点到达最长链的支链长度了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <bits/stdc++.h> #define ll long long #define MAXN 200005 using namespace std ;int head[MAXN];vector <pair<int ,int > > edge;bool vis[MAXN];int maxv,maxd;int a,b,ld;int d2a[MAXN];void dfs (int x,int d) { vis[x]=1 ; if (d>maxd){ maxd=d; maxv=x; } for (int i=head[x];i<head[x+1 ];i++){ if (!vis[edge[i].second]){ dfs(edge[i].second,d+1 ); } } return ; } void dfs2 (int x,int d) { vis[x]=1 ; d2a[x]=d; if (d>maxd){ maxd=d; maxv=x; } for (int i=head[x];i<head[x+1 ];i++){ if (!vis[edge[i].second]){ dfs2(edge[i].second,d+1 ); } } return ; } void dfs3 (int x,int d) { vis[x]=1 ; if (x!=a&&x!=b&&(d2a[x]+d-ld)/2 >maxd){ maxd=(d2a[x]+d-ld)/2 ; maxv=x; } for (int i=head[x];i<head[x+1 ];i++){ if (!vis[edge[i].second]){ dfs3(edge[i].second,d+1 ); } } return ; } int main () { int n; cin >>n; for (int i=0 ;i<n-1 ;i++){ int ta,tb; cin >>ta>>tb; edge.push_back(make_pair(ta,tb)); edge.push_back(make_pair(tb,ta)); } sort(edge.begin(),edge.end()); head[edge[0 ].first]=0 ; for (int i=1 ;i<edge.size();i++){ if (edge[i].first!=edge[i-1 ].first){ head[edge[i].first]=i; } } head[n+1 ]=edge.size(); memset (vis,0 ,sizeof (vis)); maxd=-1 ; dfs(1 ,0 ); a=maxv; memset (vis,0 ,sizeof (vis)); memset (d2a,0 ,sizeof (d2a)); maxd=-1 ; dfs2(a,0 ); b=maxv; ld=maxd; memset (vis,0 ,sizeof (vis)); maxd=-1 ; dfs3(b,0 ); cout <<maxd+ld<<'\n' <<a<<' ' <<b<<' ' <<maxv<<'\n' ; }
Codeforces Round #614 (Div. 2) A ConneR and the A.R.C. Markland-N 题解 读进来sort一下,然后lower_bound找到s层,贪心向两个方向找最近的可访问楼层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int n,s,k; cin >>n>>s>>k; int a[2333 ]; for (int i=0 ;i<k;i++){ cin >>a[i]; } sort(a,a+k); int ix=lower_bound(a,a+k,s)-a; if (ix==k||a[ix]!=s) cout <<0 <<endl ; else { int ans=1000000009 ; int p=ix; while (p!=k-1 &&a[p+1 ]==a[p]+1 ){ p++; } if (p!=k-1 &&a[p+1 ]!=a[p]+1 ) ans=min(ans,a[p]+1 -s); else if (p==k-1 &&a[p]!=n) ans=min(ans,a[p]+1 -s); p=ix; while (p!=0 &&a[p-1 ]==a[p]-1 ){ p--; } if (p!=0 &&a[p-1 ]!=a[p]-1 ) ans=min(ans,s-a[p]+1 ); else if (p==0 &&a[p]!=1 ) ans=min(ans,s-a[p]+1 ); cout <<ans<<endl ; } } }
B JOE is on TV! 题解 大胆猜想每次去掉一个,应该可以把一部分加起来证明单步是最优的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { double n; cin >>n; double ans=0 ; while (n!=0 ){ ans+=1 /(n--); } cout <<ans<<endl ; }
C NEKO’s Maze Game 题解 阻塞的方式只有岩浆块的正上(下)方、左斜上(下)方、右斜上(下)方也存在岩浆块。因此通过对可构成阻塞的岩浆块对数进行计数来判断全局是否被阻塞。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int n,q; cin >>n>>q; int mp[2 ][100005 ]; memset (mp,0 ,sizeof (mp)); int b=0 ; while (q--){ int r,c; cin >>r>>c; r--; c--; int flg; if (mp[r][c]){ flg=-1 ; mp[r][c]=0 ; }else { flg=1 ; mp[r][c]=1 ; } if (c!=0 ) b+=mp[(r+1 )%2 ][c-1 ]*flg; b+=mp[(r+1 )%2 ][c]*flg; if (c!=n-1 ) b+=mp[(r+1 )%2 ][c+1 ]*flg; if (b==0 ) cout <<"YES\n" ; else cout <<"NO\n" ; } }
D Aroma’s Search 题解 由于保证了ax 和ay 是大于或等于2的,因此实际上可能会访问到的node只有log2 (1016 )个,数量并不多,因此可以随便暴力枚举,本来还打算尺取法的。当然显而易见取的node是连续的。这道题最坑的实际上是数据范围,中间有个地方会爆掉long long,导致我被第126个点卡了好几次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> #define ll unsigned long long #define MAXN 2e17 using namespace std ;ll c_dis (ll a,ll b,ll c,ll d) { return abs ((long long )a-(long long )c)+abs ((long long )b-(long long )d); } int main () { ll a[3 ][233 ]; ll x0,y0,ax,ay,bx,by; cin >>a[0 ][0 ]>>a[1 ][0 ]>>ax>>ay>>bx>>by; a[2 ][0 ]=0 ; ll xs,ys,t; cin >>xs>>ys>>t; int p=0 ; while (a[0 ][p]*ax+bx<MAXN&&a[1 ][p]*ay+by<MAXN){ a[0 ][p+1 ]=a[0 ][p]*ax+bx; a[1 ][p+1 ]=a[1 ][p]*ay+by; a[2 ][p+1 ]=c_dis(a[0 ][p],a[1 ][p],a[0 ][p+1 ],a[1 ][p+1 ])+a[2 ][p]; p++; } int ans=0 ; for (int i=0 ;i<=p;i++){ for (int j=0 ;j<=p;j++){ unsigned long long tmp=c_dis(xs,ys,a[0 ][i],a[1 ][i])+a[2 ][j]-a[2 ][i]; if (tmp<=t) ans=max(ans,j-i+1 ); tmp=c_dis(xs,ys,a[0 ][j],a[1 ][j])+a[2 ][j]-a[2 ][i]; if (tmp<=t) ans=max(ans,j-i+1 ); } } cout <<ans<<endl ; return 0 ; }
Educational Codeforces Round 80 (Rated for Div. 2) A Deadline 题解 将总时间的函数对x 求导,然后令其等于零,得到总时间最少是2$\sqrt{d} $-1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ double n,d; cin >>n>>d; if (2 *sqrt (d)-1 <=n) cout <<"YES\n" ; else cout <<"NO\n" ; } return 0 ; }
B Yet Another Meme Problem 题解 可以解出来b的取值只能是9、99、999…而a可以是任何数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ ll A,B; cin >>A>>B; ll numb=0 ; ll tmpb=9 ; while (tmpb<=B){ numb++; tmpb=tmpb*10 +9 ; } cout <<numb*A<<endl ; } return 0 ; }
C Two Arrays 题解 用C[i][j]表示共i位,使用了1~j个数的方案数,使用记忆化搜索减少时间复杂度。显然这两个数列的整体大小关系与最后一位数字的大小关系是相同的,因此只需要枚举最后一位就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <bits/stdc++.h> #define ll long long #define MAXN 1000000007 using namespace std ;ll C[23 ][2333 ]; ll calc (int m,int n) { if (C[m][n]!=0 ) return C[m][n]; if (m==1 ) return C[m][n]=n; if (n==1 ) return C[m][n]=1 ; ll tmp=0 ; for (int i=1 ;i<=n;i++){ tmp=(tmp+calc(m-1 ,i))%MAXN; } return C[m][n]=tmp; } int main () { int n,m; cin >>n>>m; memset (C,0 ,sizeof (C)); ll ans=0 ; for (int i=1 ;i<=n;i++){ ans=(ans+(calc(m,i)-calc(m,i-1 ))*calc(m,n-i+1 )%MAXN+MAXN)%MAXN; } cout <<ans<<endl ; return 0 ; }
D Minimax Problem 题解 看似二分的check需要进行n2 m次比对,但是实际上,数列每一位只有大于或等于和小于mid两种状态,因此合计只有2m -1个数列的状态。换言之,将原数列处理之后,check实际上只有(2m -1)2 的复杂度,这里可以用状压优化一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <bits/stdc++.h> #define ll long long using namespace std ;int n,m;int a[300005 ][10 ];int g[555 ];int ans1,ans2;bool check (int x) { memset (g,0 ,sizeof (g)); for (int i=1 ;i<=n;i++){ int tmp=0 ; for (int j=0 ;j<m;j++){ if (a[i][j]>=x) tmp|=(1 <<j); } if (!g[tmp]) g[tmp]=i; } for (int i=0 ;i<(1 <<m);i++){ for (int j=0 ;j<(1 <<m);j++){ if (g[i]&&g[j]&&((i|j)==((1 <<m)-1 ))){ ans1=g[i]; ans2=g[j]; return 1 ; } } } return 0 ; } int main () { cin >>n>>m; for (int i=1 ;i<=n;i++){ for (int j=0 ;j<m;j++){ cin >>a[i][j]; } } int l=0 ,r=1e9 +9 ; while (l<=r){ int mid=(l+r)>>1 ; if (check(mid)){ l=mid+1 ; } else { r=mid-1 ; } } cout <<ans1<<' ' <<ans2<<endl ; return 0 ; }
Codeforces Round #601 (Div. 2) A Changing Volume 题解 首先大于等于五格音量的肯定优先按+/-5
然后人工处理一下,四格音量要两次+/-2;三格音量一次+/-1,一次+/-2;两格和一格都是一次
因为忘了case的语法,所以使用了嵌套if
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int a,b; cin >>a>>b; int ans=0 ; int d=abs (a-b)/5 ,m=abs (a-b)%5 ; if (m==0 ) ans=d; else if (m==1 ) ans=d+1 ; else if (m==2 ) ans=d+1 ; else if (m==3 ) ans=d+2 ; else if (m==4 ) ans=d+2 ; cout <<ans<<endl ; } return 0 ; }
B Fridge Lockers 题解 这道题当时好像出锅了,赛后开VP里面多了m≤n的限制。显然一个冰箱至少要和两个其他冰箱相连才能保证不会被别人打开,结合m≤n的限制,冰箱只能成环。特判一下n==2就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int T; cin >>T; while (T--){ int n,m; cin >>n>>m; int ans=0 ,tmp; for (int i=0 ;i<n;i++){ cin >>tmp; ans+=tmp; } ans*=2 ; if (n==2 ||m<n) cout <<-1 <<endl ; else { cout <<ans<<endl ; for (int i=1 ;i<n;i++) cout <<i<<' ' <<i+1 <<endl ; cout <<n<<' ' <<1 <<endl ; } } return 0 ; }
C League of Leesins 题解 纯模拟,因为开头只出现过一次,所以找出来然后一个一个推过去就行。
懒得想变量名所以代码写的很丑陋。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <bits/stdc++.h> #define ll long long using namespace std ;struct tgadata { int num,q[3 ]; }tga[100005 ],agt[100005 ]; int findnxt (int a,int b, int c) { for (int i=0 ;i<agt[b].num;i++){ for (int j=0 ;j<agt[c].num;j++){ if (agt[b].q[i]==agt[c].q[j]){ for (int k=0 ;k<3 ;k++){ if (tga[agt[b].q[i]].q[k]!=b&&tga[agt[b].q[i]].q[k]!=c){ if (tga[agt[b].q[i]].q[k]!=a) return tga[agt[b].q[i]].q[k]; break ; } } } } } return 0 ; } int main () { int n; cin >>n; for (int i=0 ;i<n-2 ;i++){ for (int j=0 ;j<3 ;j++){ cin >>tga[i].q[j]; agt[tga[i].q[j]].q[agt[tga[i].q[j]].num++]=i; } tga[i].num=i; } int p1; for (int i=0 ;i<n-2 ;i++){ if (agt[i].num==1 ){ p1=i; break ; } } int p2; for (int i=0 ;i<3 ;i++) { if (tga[agt[p1].q[0 ]].q[i]!=p1&&agt[tga[agt[p1].q[0 ]].q[i]].num<=2 ){ p2=tga[agt[p1].q[0 ]].q[i]; break ; } } int p3; for (int i=0 ;i<3 ;i++) { if (tga[agt[p1].q[0 ]].q[i]!=p1&&tga[agt[p1].q[0 ]].q[i]!=p2){ p3=tga[agt[p1].q[0 ]].q[i]; break ; } } cout <<p1; for (int i=0 ;i<n-3 ;i++){ int tmp=findnxt(p1,p2,p3); p1=p2; p2=p3; p3=tmp; cout <<' ' <<p1; } cout <<' ' <<p2<<' ' <<p3<<endl ; return 0 ; }
Hello 2020 A New Year and Naming 题解 类似中国的天干地支纪年法,将y分别对n和m取模然后对应到名字即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> #define ll long long using namespace std ;int main () { int n,m; cin >>n>>m; string a[23 ],b[23 ]; for (int i=0 ;i<n;i++){ cin >>a[i]; } for (int i=0 ;i<m;i++){ cin >>b[i]; } int q; cin >>q; int y; while (q--){ cin >>y; string ans=a[(y-1 )%n]+b[(y-1 )%m]+"\n" ; cout <<ans; } return 0 ; }
B New Year and Ascent Sequence 题解 将序列分为两类,一种是本身存在上升数对的,记有a个;一种是本身不存在上升数对的,记有b个。
对于前一种和前一种排列,任意排列均可,因此有a*a个可行解;对于前一种和后一种排列,任意排列均可,因此有a*b个可行解;对于后一种和后一种排列,需要满足前一个序列的最小值小于或等于后一个的最大值,这个需要枚举计算。
对于计算第三类排列,考虑到我们并不关心到底是谁和谁排列,只关心能排列出多少种,因此可以只保留每个序列的最大和最小值这一信息。接下来,先将最大和最小值数列分别升序排列,注意到可以利用数列的单调性降低枚举复杂度(暴力为O(n^2^))。对于某个特定的最小值,我们找到了一个刚好大于它的最大值,那么比这个值更大的均可以成为可行解;而比当前选取的最小值更大的最小值,能与其形成可行解的最大值必然在之前选取的最大值之后。通过单调性,我们只需要将序列枚举一遍,复杂度降为O(n)。
比赛时由于没考虑到第一类可行解,导致在debug的时候缝缝补补,代码很丑陋,变量名也在乱来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <bits/stdc++.h> #define ll long long using namespace std ;int smin[100005 ],smax[100005 ];int main () { ll n; cin >>n; ll nn=0 ,nnn=0 ; for (int j=0 ;j<n;j++){ int l; cin >>l; bool flg=1 ; int minn=1000006 ,maxx=-1 ; for (int i=0 ;i<l;i++){ int tmp; cin >>tmp; if (flg&&tmp>minn){ nnn++; flg=0 ; } minn=min(tmp,minn); maxx=max(tmp,maxx); } if (flg){ smin[nn]=minn; smax[nn]=maxx; nn++; } }; sort(smin,smin+nn); sort(smax,smax+nn); ll ans=0 ; ll p1=0 ,p2=0 ; while (p1!=nn){ while (p1!=nn&&p2!=nn&&smin[p1]>=smax[p2]){ p2++; } ans+=nn-p2; p1++; } ans+=nnn*nn*2 +nnn*nnn; cout <<ans<<"\n" ; return 0 ; }
C New Year and Permutation 题解 首先不能把思路放在对于特定的数列怎么求其满足题意的子序列数量,而是直接思考如何构造满足题意的子序列。
因为r-l正好是max-min,而每个数又是不一样的,所以显然l到r这个区间里的数也必须正好是连续的。
我们考虑这段连续的数字的个数是i,那么这段数字有n-i+1种取法,其排列方式有 i! 种,而对于剩下的n-i个数字,其排列在选出的这段数字区间两端,首先,其排列有 (n-i)! 种,然后用插板法将其分为两份放在选定数字区间的两侧,因此还要再乘以(n-i+1)。
因此最终答案为 Σn i=1 ( i! * (n-i+1) * (n-i+1)! )。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <bits/stdc++.h> #define ll long long using namespace std ;ll jc[250004 ]; int main () { ll n,m; cin >>n>>m; jc[0 ]=1 ; for (ll i=1 ;i<=n;i++){ jc[i]=jc[i-1 ]*i%m; } ll ans=0 ; for (ll i=1 ;i<=n;i++){ ans=(ans+(jc[i]*jc[n-i+1 ]%m)*(n-i+1 )%m)%m; } cout <<ans<<endl ; return 0 ; }
D New Year and Conference 题解 一共有两种NO的情况,分别是两个演讲在A场地冲突,在B场地不冲突;在B场地冲突,在A场地不冲突。所以需要判断两次,但其实判断流程是一样的。
那前一种NO的情况做例子,我们先将每场演讲在A场地的开始和结束的时间节点按照时间顺序排列,同时将演讲的序号也记录进去。有点像括号匹配,我们顺序枚举每个时间节点,用一个Set记录当前有哪些演讲在讲,然后每当碰到一个时间节点具有未出现的演讲序号,就加进去,碰到已出现的就删除Set里的对应的演讲。这就意味着,同时出现在Set中的演讲序号其对应的演讲在A场地是冲突的。
那么如何处理这些演讲是否在B场地也冲突呢,我们用一个线段树维护每个时间节点有多少演讲,即每当Set中加入一场演讲,我们就把其在B场地演讲的时间段包含的时间节点全部加一,每当删除就全部减一。因为演讲只要在一个节点全部冲突,那么就是全部冲突,所以我们只要询问当前线段树所有节点的最大值,如果最大值等于Set中的演讲数量,那么他们必然在B中至少有一个时间节点全部冲突,否则不存在一个全部冲突的时间节点。这样就可以解决Set中演讲是不是同时在B场地全部冲突。区间修改,区间查询最值的线段树是模板,不再赘述。
由于数值的范围,需要对数据离散化,这也是为什么前文中使用的词语是时间节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 #include <bits/stdc++.h> using namespace std ;struct cfdata { int sa,ea,sb,eb; }cf[100005 ]; pair<int ,int > cfina[200005 ],cfinb[200005 ]; bool cmp (pair<int ,int > a,pair<int ,int > b) { return a.second<b.second; } vector <int > trans;int gettrans (int x) { return lower_bound(trans.begin(),trans.end(),x)-trans.begin()+1 ; } int f[2001005 ],tag[2001005 ];void downtag (int x) { if (tag[x]==0 ) return ; tag[x*2 ]+=tag[x]; f[x*2 ]+=tag[x]; tag[x*2 +1 ]+=tag[x]; f[x*2 +1 ]+=tag[x]; tag[x]=0 ; } void modify (int x,int l,int r,int ll,int rr,int d) { if (ll<=l&&r<=rr){ f[x]+=d; tag[x]+=d; } else { downtag(x); int mid=(l+r)/2 ; if (ll<=mid) modify(x*2 ,l,mid,ll,rr,d); if (rr>=mid+1 ) modify(x*2 +1 ,mid+1 ,r,ll,rr,d); f[x]=max(f[x*2 ],f[x*2 +1 ]); } } int main () { int n; cin >>n; for (int i=0 ;i<n;i++){ cin >>cf[i].sa>>cf[i].ea>>cf[i].sb>>cf[i].eb; trans.push_back(cf[i].sa); trans.push_back(cf[i].ea); trans.push_back(cf[i].sb); trans.push_back(cf[i].eb); cfina[i]=make_pair(i,cf[i].sa); cfina[n+i]=make_pair(i,cf[i].ea); cfinb[i]=make_pair(i,cf[i].sb); cfinb[n+i]=make_pair(i,cf[i].eb); } sort(trans.begin(),trans.end()); trans.erase(unique(trans.begin(),trans.end()),trans.end()); sort(cfina,cfina+n*2 ,cmp); sort(cfinb,cfinb+n*2 ,cmp); set <int > cfinnow; memset (f,0 ,sizeof (f)); memset (tag,0 ,sizeof (tag)); for (int i=0 ;i<n*2 ;i++){ if (cfinnow.find(cfina[i].first)!=cfinnow.end()){ cfinnow.erase(cfina[i].first); modify(1 ,1 ,trans.size(),gettrans(cf[cfina[i].first].sb),gettrans(cf[cfina[i].first].eb),-1 ); }else { cfinnow.insert(cfina[i].first); modify(1 ,1 ,trans.size(),gettrans(cf[cfina[i].first].sb),gettrans(cf[cfina[i].first].eb),1 ); if (f[1 ]!=cfinnow.size()){ cout <<"NO" <<endl ; return 0 ; } } } cfinnow.clear(); memset (f,0 ,sizeof (f)); memset (tag,0 ,sizeof (tag)); for (int i=0 ;i<n*2 ;i++){ if (cfinnow.find(cfinb[i].first)!=cfinnow.end()){ cfinnow.erase(cfinb[i].first); modify(1 ,1 ,trans.size(),gettrans(cf[cfinb[i].first].sa),gettrans(cf[cfinb[i].first].ea),-1 ); }else { cfinnow.insert(cfinb[i].first); modify(1 ,1 ,trans.size(),gettrans(cf[cfinb[i].first].sa),gettrans(cf[cfinb[i].first].ea),1 ); if (f[1 ]!=cfinnow.size()){ cout <<"NO" <<endl ; return 0 ; } } } cout <<"YES" <<endl ; return 0 ; }
Codeforces Round #590 (Div. 3) D Distinct Characters Queries 题解 用26个BIT维护一下。
然而比赛的时候给B题整自闭了(循环写成i–,结果一直找不到哪儿错了),最后被忽悠说一定要用线段树就没做。
补题的时候还把memset()的参数顺序搞错了,又查了半天(VSCode把我养成five了,参数次次记不得)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 #include <bits/stdc++.h> #define ll long long using namespace std ;int f[100005 ][30 ];int lowbit (int x) { return x&(-x); } void modify (int x,int c,int d,int len) { while (x<=len){ f[x][c]+=d; x+=lowbit(x); } return ; } void query (int x,int *tf) { while (x!=0 ){ for (int i=0 ;i<26 ;i++){ tf[i]+=f[x][i]; } x-=lowbit(x); } return ; } int main () { char s[100005 ]; cin >>s; int len=strlen (s); memset (f,0 ,sizeof (f)); for (int i=0 ;i<len;i++){ modify(i+1 ,s[i]-'a' ,1 ,len); } int n; cin >>n; int t,p,l,r; char ch; int tfl[30 ],tfr[30 ]; int ans; for (int i=0 ;i<n;i++){ cin >>t; if (t==1 ){ cin >>p>>ch; modify(p,s[p-1 ]-'a' ,-1 ,len); s[p-1 ]=ch; modify(p,s[p-1 ]-'a' ,1 ,len); } else { cin >>l>>r; memset (tfl,0 ,sizeof (tfl)); query(l-1 ,tfl); memset (tfr,0 ,sizeof (tfr)); query(r,tfr); ans=0 ; for (int j=0 ;j<26 ;j++){ if (tfl[j]!=tfr[j]){ ans++; } } cout <<ans<<endl ; } } }