比赛链接:
http://codeforces.com/gym/100712
题目链接:
http://codeforces.com/gym/100712/attachments/download/3454/acm-amman-collegiate-programming-contest-en.pdf
直接排序,复杂度O(nlogn)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; struct team { string name; int s,p; bool operator < (const team &t)const { return s==t.s ? p<t.p : s>t.s; } }t[105]; int main() { ios::sync_with_stdio(false); int T; cin>>T; while(T--) { int N; cin>>N; for(int i=1;i<=N;i++) { cin>>t[i].name>>t[i].s>>t[i].p; } sort(t+1,t+N+1); cout<<t[1].name<<endl; } return 0; }
B. Rock-Paper-Scissors
分别预处理前k(k=0,1,2,...,n)次均出剪刀、石头或布的得分,O(n^2)枚举分界点即可。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; char s[1005]; int cnt[3][1005]; int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); scanf("%s",s); for(int i=1;i<=n;i++) { cnt[0][i]=cnt[0][i-1]+(s[i-1]=='S')-(s[i-1]=='P'); cnt[1][i]=cnt[1][i-1]+(s[i-1]=='R')-(s[i-1]=='S'); cnt[2][i]=cnt[2][i-1]+(s[i-1]=='P')-(s[i-1]=='R'); } int ans=0; for(int i=0;i<=n;i++) { for(int j=i;j<=n;j++) { //printf("%d %d\n",i,j); if(cnt[0][i]+cnt[1][j]-cnt[1][i]+cnt[2][n]-cnt[2][j]>0)ans++; } } printf("%d\n",ans); } return 0; }
C. Street Lamps
将所有被路灯找到的格子标记为"*",于是每一段连续的k个"."对答案的贡献为ceil(k/3),复杂度O(n)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; char s[105]; bool isok[105]; int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); scanf("%s",s); memset(isok,0,sizeof(isok)); for(int i=0;i<n;i++) { if(i>0 && s[i-1]=='*')isok[i]=1; if(s[i]=='*')isok[i]=1; if(i<n-1 && s[i+1]=='*')isok[i]=1; } int loc=0,cnt=0,ans=0; while(loc<n) { while(loc<n && isok[loc]) { ans+=(cnt+2)/3; cnt=0; loc++; } if(loc<n) { cnt++; loc++; } } ans+=(cnt+2)/3; printf("%d\n",ans); } return 0; }
D. Alternating Strings
记dp[i]为将序列前i个字符按照要求划分所需要的最少段数,
若子串s[j..(i-1)]为“交替串”,则有dp[i]=max(dp[i],dp[j]+(i-j)),否则有dp[i]=max(dp[i],dp[j]+1),
暴力枚举j转移即可,复杂度O(n^2)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int MAXN=1005; char s[MAXN]; int dp[MAXN]; int main() { int T; scanf("%d",&T); while(T--) { int n,k; scanf("%d%d",&n,&k); scanf("%s",s); memset(dp,0x3f3f3f3f,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++) { bool isok=0; for(int j=i-1;j>=max(0,i-k);j--) { if(j<i-1 && s[j]==s[j+1])isok=1; if(isok)dp[i]=min(dp[i],dp[j]+1); else dp[i]=min(dp[i],dp[j]+(i-j)); } } printf("%d\n",dp[n]-1); } return 0; }
E. Epic Professor
找出最大的数k,将所有数加上100-k之后统计>=50的数的个数即可,复杂度O(n)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; int a[105]; int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); int mm=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); mm=max(mm,a[i]); } int add=100-mm; int ans=0; for(int i=1;i<=n;i++) { if(a[i]+add>=50)ans++; } printf("%d\n",ans); } return 0; }
F. Travelling Salesman
最小瓶颈生成树,跑一遍Kruskal即可,复杂度O(mlogm)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int MAXN=100005; const int MAXM=100005; struct Edge { int u,v,c; bool operator < (const Edge &t)const { return c<t.c; } }e[MAXM]; int p[MAXN]; void Init(int n) { for(int i=1;i<=n;i++)p[i]=i; } int Find(int x) { return x==p[x] ? x : p[x]=Find(p[x]); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c); sort(e,e+m); int ans=0,dif=n; Init(n); for(int i=0;i<m;i++) { int t1=Find(e[i].u); int t2=Find(e[i].v); if(t1!=t2) { p[t1]=t2; dif--; } ans=max(ans,e[i].c); if(dif==1)break; } printf("%d\n",ans); } return 0; }
G. Heavy Coins
暴力枚举所有硬币构成的集合,统计从该集合删去任意一枚硬币后总价值<=S的方案数,复杂度O(n*2^n)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; int a[15]; int main() { int T; scanf("%d",&T); while(T--) { int n,s; scanf("%d%d",&n,&s); for(int i=0;i<n;i++)scanf("%d",&a[i]); int ans=0; for(int mask=0;mask<(1<<n);mask++) { int tot=0,cnt=0; for(int i=0;i<n;i++) if(mask&(1<<i)) { cnt++; tot+=a[i]; } if(tot>=s) { bool isok=1; for(int i=0;i<n;i++) if(mask&(1<<i)) if(tot-a[i]>=s)isok=0; if(isok)ans=max(ans,cnt); } } printf("%d\n",ans); } return 0; }
H. Bridges
对原图做一次双连通分量缩点,对缩点后得到树做两次BFS找出直径,该直径的长度即为最多可以减少的割边数,复杂度O(n+m)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<queue> using namespace std; const int MAXN=100005; const int MAXM=200005; struct Edge { int to,next; bool cut; }edge[MAXM]; int head[MAXN],tot; int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN]; int Index,top; int block; bool Instack[MAXN]; int bridge; void addedge(int u,int v) { edge[tot].to=v; edge[tot].next=head[u]; edge[tot].cut=0; head[u]=tot++; } void Tarjan(int u,int pre) { int v; Low[u]=DFN[u]=++Index; Stack[top++]=u; Instack[u]=1; for(int i=head[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(v==pre)continue; if(!DFN[v]) { Tarjan(v,u); if(Low[u]>Low[v])Low[u]=Low[v]; if(Low[v]>DFN[u]) { bridge++; edge[i].cut=1; edge[i^1].cut=1; } } else if(Instack[v] && Low[u]>DFN[v]) Low[u]=DFN[v]; } if(Low[u]==DFN[u]) { block++; do { v=Stack[--top]; Instack[v]=0; Belong[v]=block; } while(v!=u); } } vector<int>e[MAXN]; void init(int n) { for(int i=1;i<=n;i++)e[i].clear(); tot=0; memset(head,-1,sizeof(head)); } int dis[MAXN]; void bfs(int st) { queue<int>q; q.push(st); dis[st]=0; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<e[u].size();i++) { if(dis[e[u][i]]>dis[u]+1) { q.push(e[u][i]); dis[e[u][i]]=dis[u]+1; } } } } void solve(int n) { memset(DFN,0,sizeof(DFN)); memset(Instack,0,sizeof(Instack)); Index=top=block=bridge=0; Tarjan(1,0); for(int i=1;i<=n;i++) for(int j=head[i];j!=-1;j=edge[j].next) if(edge[j].cut) { e[Belong[edge[j].to]].push_back(Belong[i]); e[Belong[i]].push_back(Belong[edge[j].to]); } memset(dis,0x3f3f3f3f,sizeof(dis)); bfs(1); int ans=0,loc=1; for(int i=1;i<=block;i++) { if(dis[i]>ans) { loc=i; ans=dis[i]; } } memset(dis,0x3f3f3f3f,sizeof(dis)); bfs(loc); ans=0; for(int i=1;i<=block;i++)ans=max(ans,dis[i]); printf("%d\n",bridge-ans); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); init(n); int u,v; for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } solve(n); } return 0; }
I. Bahosain and Digits
枚举k,枚举操作完成后所有位的结果,利用标记保证每一次检验的效率,复杂度O(10*n^2)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; char t[255],s[255]; int lazy[255]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%s",t); int n=strlen(t); int res=0; bool isok=0; for(int k=n;k>=1 && !isok;k--) { for(int d=0;d<=9 && !isok;d++) { strcpy(s,t); memset(lazy,0,sizeof(lazy)); int cur=0; for(int i=0;i<=n-k;i++) { s[i]=(s[i]-'0'+cur%10+10)%10+'0'; lazy[i]+=d+10-(s[i]-'0'); lazy[i+k-1]-=d+10-(s[i]-'0'); cur+=lazy[i]; } for(int i=n-k+1;i<n;i++) { s[i]=(s[i]-'0'+cur%10+10)%10+'0'; cur+=lazy[i]; } //printf("%s\n",s); bool flag=1; for(int i=n-k+1;i<n;i++) if(s[i]-'0'!=d)flag=0; if(flag) { isok=1; res=k; } } } printf("%d\n",res); } return 0; }
J. Candy
直接维护两个优先队列,复杂度O(nlogn+mlogm)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<map> using namespace std; map<int,int>s,p; map<int,int>::iterator itr_s,itr_p; int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); int in; s.clear(); p.clear(); for(int i=1;i<=n;i++) { scanf("%d",&in); s[in]++; } for(int i=1;i<=m;i++) { scanf("%d",&in); p[in]++; } itr_s=s.begin(); itr_p=p.begin(); while(itr_s!=s.end() && itr_p!=p.end()) { while(itr_p!=p.end() && itr_s->second > itr_p->second)p.erase(itr_p++); if(itr_p==p.end())break; s.erase(itr_s++); p.erase(itr_p++); } printf("%s\n",(s.empty() ? "YES" : "NO")); } return 0; }
K. Runtime Error
记录每个数的出现次数,逐个枚举序列中的元素,注意x==0以及x==y的情形,复杂度O(n)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; int a[100005]; int x[100005]; int main() { int T; scanf("%d",&T); while(T--) { int n,k; scanf("%d%d",&n,&k); memset(x,0,sizeof(x)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); x[a[i]]++; } int mm=0x3f3f3f3f; for(int i=1;i<=n;i++) { if(a[i]==0)continue; if(k%a[i]!=0)continue; if(x[k/a[i]]>1-(k/a[i]!=a[i]))mm=min(mm,a[i]); } if(mm==0x3f3f3f3f)printf("-1\n"); else printf("%d %d\n",mm,k/mm); } return 0; }
L. Alternating Strings II
此题是D题的加强版,
注意到“交替串”的长度具有二分性质,
如果用一个序列记录前i个字符中有多少对相邻的字符是不同的,
那么对于一个固定的i,可以二分出最小的j使得s[j..(i-1)]是“交替串”,
仍考虑D题的dp方程,
利用线段树分别维护dp[i]的最小值以及dp[i]-i的最小值,
得到分界点j之后,可以利用线段树上的区间查询加速转移,
复杂度O(nlogn)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int MAXN=100005; const int INF=0x3f3f3f3f; char str[MAXN]; int pre[MAXN],dp[MAXN]; struct node { int l,r,m,v[2]; }s[MAXN<<2]; void push_up(int n) { for(int i=0;i<2;i++) s[n].v[i]=min(s[n<<1].v[i],s[n<<1|1].v[i]); } void build(int l,int r,int n) { int m=(l+r)>>1; s[n].l=l; s[n].r=r; s[n].m=m; if(r-l==1) { s[n].v[0]=s[n].v[1]=INF; return; } build(l,m,n<<1); build(m,r,n<<1|1); } void update(int k,int p,int n) { if(s[n].l==p && s[n].r==p+1) { s[n].v[0]=min(s[n].v[0],k); s[n].v[1]=min(s[n].v[1],k-(p-1)); return; } if(p<s[n].m)update(k,p,n<<1); else update(k,p,n<<1|1); push_up(n); } int query(int l,int r,int n,int op) { if(r<=l)return INF; if(s[n].l==l && s[n].r==r)return s[n].v[op]; if(r<=s[n].m)return query(l,r,n<<1,op); if(l>=s[n].m)return query(l,r,n<<1|1,op); return min(query(l,s[n].m,n<<1,op),query(s[n].m,r,n<<1|1,op)); } int main() { int T; scanf("%d",&T); while(T--) { int n,k; scanf("%d%d",&n,&k); scanf("%s",str); pre[0]=0; for(int i=1;i<n;i++) pre[i]=pre[i-1]+(str[i]!=str[i-1]); memset(dp,INF,sizeof(dp)); build(1,n+2,1); //printf("build done\n"); dp[0]=0; update(dp[0],1,1); for(int i=1;i<=n;i++) { //printf("work on %d\n",i); int l=max(i-k,0),r=i-1; while(l<r) { int m=(l+r)>>1; if(pre[i-1]-pre[m]==i-m-1)r=m; else l=m+1; } //printf("left most %d\n",l); dp[i]=min(dp[i],query(max(i-k,0)+1,l+1,1,0)+1); dp[i]=min(dp[i],query(l+1,i+1,1,1)+i); //printf("%d %d %d\n",query(max(i-k,0)+1,l+2,1,0),query(l+2,i+1,1,1),dp[i]); update(dp[i],i+1,1); } printf("%d\n",dp[n]-1); } return 0; }