B
给定一个数列,每次操作选择一个偶数cc,把所有与cc相同的数都除以22,重复此类操作,直到所有数都变为奇数,问最小的操作次数。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+10; int a[maxn]; ll n; unordered_map<int,int>vis; bool cmp(int a,int b){ return a>b; } int main() { ll t; cin>>t; while(t--){ cin>>n; vis.clear(); ll ans=0; for(int i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++){ if(vis[a[i]]==0&&(a[i]&1)==0){ vis[a[i]]=1; while(a[i]){ ans++;a[i]/=2;vis[a[i]]=1; if(a[i]&1) break; } } } cout<<ans<<endl; } }
C
给定一个字符串,要求去掉若干个字母,使得该字符串不包含"one""one"和"two""two",求最小的去掉字符的数量。
有以下三个情况:
(1):one,ooone这种,那直接取消n或者e,而不是o
(2):two,twooo这种,那直接取消t或者w,而不是o
(3)twone直接取消o;
用kmp先匹配twone,因为先匹配one和two的话twone这个还得再搞一次。
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } char one[4]={"one"}; char two[4]={"two"}; char twone[6]={"twone"}; int nxtone[4],nxttwo[4],nxttwone[6]; const int N=2e5+8; char s[N]; int len,cnt; int anspos[N]; void makenext(char qaq[],int nxt[],int n){ nxt[0]=0; int k=0; for (int i=1;i<n;i++){ while ((k>0)&&(qaq[k]!=qaq[i])) k=nxt[k-1]; if (qaq[k]==qaq[i]) k++; nxt[i]=k; } } void kmp(char qaq[],int nxt[],char qwq[],int n){ int q=0; for (int i=0;i<len;i++){ if (qwq[i]=='@') continue; while ((qwq[i]!=qaq[q])&&(q>0)) q=nxt[q-1]; if (qwq[i]==qaq[q]) q++; if (q==n) { if (n==5) {qwq[i-2]='@'; anspos[++cnt]=i-1;} else {qwq[i-1]='@'; anspos[++cnt]=i;} } } } void Input(void) { cnt=0; scanf("%s",s); len=strlen(s); } void Solve(void) { kmp(twone,nxttwone,s,5); kmp(two,nxttwo,s,3); kmp(one,nxtone,s,3); } void Output(void) { write(cnt,' '); for(int i=1;i<=cnt;++i) printf("%d%c",anspos[i],i==cnt?' ':' '); if (cnt==0) puts(""); } main(void) { int kase; read(kase); makenext(one,nxtone,3); makenext(two,nxttwo,3); makenext(twone,nxttwone,5); for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); } }
D
题目大意
给定若干个互不相同的0101子串,现翻转一些子串,使得这些子串可以排成一行,第一个子串任意,然后前一个子串的末数字和后一个子串的首数字一样,且不能有相同的子串。求最小的翻转次数,且输出任一种对应的方案。无解则输出−1−1
解题思路
考虑到只涉及子串的首位和末位,我们按照首位和末位的数字将这些子串分为44类,即00,01,10,1100,01,10,11串。
无解的情况就是有0000串和1111串但无1010和0101串。
由植树原理知只要1010串和0101串的数量差值小于等于11即可。
因为0101串翻转就是1010串,那么最小的操作次数就是1010和0101串的数量差的一半下取整。但这里可能会出现翻转后的子串与已有的子串相同。
因为0000串和1111串无需翻转,我们就不用考虑它们。
注意到如果一个0101串翻转后与已有的子串相同,这个已有的子串一定是1010串,且可以和翻转的0101串进行匹配,我们就可以不考虑这对子串了。
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; const int N=2e5+8; int num[4],s01[N],s10[N]; int n,l,c0,c1; string s,ss; unordered_map<string,pair<int,bool>> qwq; void Input(void) { qwq.clear(); num[0]=num[1]=num[2]=num[3]=0; c0=c1=0; cin>>n; for(int i=1;i<=n;++i){ cin>>s; if (s[0]==s[s.size()-1]) {qwq[s]=make_pair(i,1);continue;} ss=s; reverse(s.begin(),s.end()); if (qwq[s].second==1) {qwq[s].second=0;++num[2];++num[3];continue;} qwq[ss]=make_pair(i,1); } } void Solve(void) { for(auto v:qwq){ if (v.second.second==1){ const char *ss=v.first.c_str(); l=strlen(ss); if (l==1) ++num[ss[0]-'0']; else{ if (ss[0]=='0'&&ss[l-1]=='0') ++num[0]; else if (ss[0]=='0'&&ss[l-1]=='1') ++num[2],s01[++c0]=v.second.first; else if (ss[0]=='1'&&ss[l-1]=='0') ++num[3],s10[++c1]=v.second.first; else ++num[1]; } } } if (num[2]==0&&num[3]==0&&num[0]!=0&&num[1]!=0) cout<<"-1"<<endl; else { int qwq=ABS(num[2]-num[3]); qwq/=2; cout<<qwq<<endl; if (num[2]>num[3]) for(int i=1;i<=qwq;++i) cout<<s01[i]<<(i==qwq?' ':' '); else for(int i=1;i<=qwq;++i) cout<<s10[i]<<(i==qwq?' ':' '); if (qwq==0) cout<<' '; } } void Output(void) {} main(void) { ios::sync_with_stdio(false); int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); cin>>kase; for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); } }
E
题目大意
给定一张nn个点mm条边的无向图,有两个特殊城市a,ba,b,求点对数(x,y)(x,y),使得从城市xx到城市yy的所有路径中都会经过城市aa和bb,其中xx与yy均不等于aa和bb。
解题思路
我们考虑城市aa和bb,很容易想到一种情形就是aa和bb把它们之间的点形成了孤岛,这样从aa的另外一边的城市到bb的另外一边的城市就必须经过aa和bb了。而aa,bb就成了连接它们的割点。
那么我们就对aa进行DFSDFS,记录aa不经过bb所能到达的城市,有cntacnta个,然后再从bb进行DFSDFS,不经过aa,如果到达了aa所到达的城市,那么这个城市就是孤岛上的城市,记有dd个,否则就是aa所到达不到,即bb的另一边的城市,记有cntbcntb个,而a的另一边的城市有cnta−dcnta−d个。
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=2e5+8; vector<int> edge[N]; int n,m,a,b; int sign[N]; long long cnta,cntb; void Input(void) { read(n); read(m); read(a); read(b); for(int i=1;i<=n;++i) edge[i].clear(); for(int u,v,i=1;i<=m;++i){ read(u); read(v); edge[u].push_back(v); edge[v].push_back(u); } } void DFSa(int x){ for(auto i:edge[x]){ if (i==b) continue; if (sign[i]==0){ sign[i]=1; ++cnta; DFSa(i); } } } void DFSb(int x){ for(auto i:edge[x]){ if (i==a) continue; if (sign[i]!=2){ if (sign[i]==1) --cnta; else ++cntb; sign[i]=2; DFSb(i); // continue; } } } void Solve(void) { for(int i=1;i<=n;++i) sign[i]=0; cnta=cntb=0; sign[a]=1; DFSa(a); sign[b]=2; DFSb(b); } void Output(void) { write(cntb*cnta,' '); } int main(void) { //ios::sync_with_stdio(false); int kase; read(kase); for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); } return 0; }
那么答案就是(cnta−d)∗cntb(cnta−d)∗cntb.