比赛链接:
http://codeforces.com/gym/100712
题目链接:
直接排序,复杂度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;
}