题目链接:传送门
【题意】:
给你n首歌,每首歌都有各自的长度length和美感beauty。
然后让你选择出最多k首出来,使得观赏度达到最大值。
观赏度 = (选择的歌曲 的长度 )× (在所选歌曲中美感最低的)
【题解】:
其实一开始以为简单的排序,美感降序,然后选择之前的歌曲即可。
但是怎么确保前面K-1首,一定是时间总和最长呢???
这就用Set来维护,一直把时间最短的排出,在过程中取最大值即可。
#include<bits/stdc++.h>
#define F first
#define S second
#define mp(x,y) make_pair(x,y)
typedef long long ll;
#define PII pair<ll,ll>
const int N = 3e5+100;
using namespace std;
PII a[N];
bool cmp( PII u,PII v){
return u.S > v.S ;
}
int main()
{
ll n,k,u,v;
cin >> n >> k ;
for(int i=1;i<=n;i++){
cin >> u >> v;
a[i] = mp(u,v);
}
sort( a+1,a+1+n,cmp);
set <PII> S;
ll num = 0 ,cur = 0;
ll ans = -1;
PII tmp ;
for(int i=1;i<=n;i++){
cur = a[i].S;
num = num + a[i].F;
S.insert(mp(a[i].S,i));
if ( S.size() > k ){
auto it = S.begin() ;
num = num - it->first;
S.erase(it);
}
ans = max( ans , num * cur );
}
printf("%lld\n",ans);
return 0;
}
PolandBall and Forest
传送门
【题意】:
有很多树的节点,然后各自有一个方向,一些向左,一些向右。
每个节点都说自己是第a[i]个节点为一棵树的。
请问有多少颗树。
【题解】:
简单的并查集弄一下。别忘记了,记住find(i),因为过程中没有把所有的节点都聚在根节点上。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int pre[N];
int a[N];
int Find(int x){
return x==pre[x] ? x : Find( pre[x] );
}
void Merge(int x,int y){
int Fx = Find(x);
int Fy = Find(y);
if( Fx!=Fy ) {
pre[Fx] = Fy;
}
}
int vis[N]={0};
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
pre[i] = i;
scanf("%d",&a[i]);
}
int ans = 0;
for(int i=n;i>=1;i--){
Merge(i,a[i]);
}
for(int i=1;i<=n;i++){
if(vis[Find(i)]==0){
ans ++;
vis[Find(i)] = 1;
}
}
printf("%d\n",ans);
return 0;
}
Serval and Parenthesis Sequence
题目链接:传送门
【题目】:
填?号,所有的前缀 (除整个字符串外)都不能是合法的括号序列。
首先有没有一个合法的填法?如果没有输出“:(”
如果有请输出那一种方案。
【题解】:
我发现这个题非常有趣,主要是因为我一直都没有想明白的如何贪心来填'?',其实把所有的左括号填完,再把所有的又括号填完即可,一点都不用关心那个所谓的前缀问题。只要在过程中加以判断即可。
原因是,只要第一个和最后一个是一对括号,中间的只要是合法即可。根据这样的想法出来的一定是符合题意的。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+100;
char s[N];
int main()
{
int n,f=1,cnt=0;
scanf("%d",&n);
scanf("%s",s);
if( n&1 || (s[0]==')')|| (s[n-1]=='(')){
f=0;
}
int L = 0,R = 0;
for(int i=0;s[i];i++){
if ( s[i] == '(') L ++;
else if ( s[i] == ')') R ++;
}
L = n/2 - L;
R = n/2 - R;
int tmp=0;
for(int i=0;s[i];i++){
if( s[i]=='?' && L ){
s[i] = '(' ;
L--;
}else if ( s[i]=='?' && R ){
s[i] = ')' ;
R--;
}
if(s[i]=='(') tmp++;
else tmp--;
if ( tmp==0 && s[i+1]!='\0' ) f=0;
}
f &= (tmp==0);
f?printf("%s\n",s):printf(":(\n");
return 0;
}
Two Teams
题目链接
【题意】:
就是两个教练选人,选人规则有2条。
1、最分数最高的
2、在附近(左和右)同时也选k个人
然后问:最后哪个人到了哪一个队伍。
【题解】:
用数组直接模拟这个移除的过程,然后用L[N],R[N]两个数组维护左右即可。
#include<bits/stdc++.h>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N = 2e5+100;
typedef pair<int,int> PII;
int L[N],R[N],ans[N],a[N],Ind[N];
int main()
{
ios_base :: sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
int n,k;
cin>>n>>k;
set <int> S;
for(int i=1;i<=n;i++) {
cin>>a[i];
L[i] = i-1;
R[i] = i+1;
Ind[a[i]] = i;
S.insert(a[i]);
}
int No = 0,K,tL,tR,tmp;
while( !S.empty() ){
auto it = S.end();
it -- ;
tmp = *it;
No = (No)%2 + 1 ;
K=k;
//printf("It %d ,%d , L : %d , R : %d \n",Ind[tmp],tmp,L[Ind[tmp]],R[Ind[tmp]]);
ans[Ind[tmp]] = No;
tL = tR = Ind[tmp];
for( int i = L[ Ind[tmp] ] ; K && i!=0; K--, i=L[i] ){
//printf("##1 %d , %d L : %d , R :%d \n",i,a[i],L[i],R[i]);
ans[i] = No;
tL = i ;
S.erase(a[i]);
}
K=k;
for( int i = R[ Ind[tmp] ] ; K && i!=n+1; K--, i=R[i] ){
//printf("@@2 %d , %d L : %d , R :%d \n",i,a[i],L[i],R[i]);
ans[i] = No;
tR = i;
S.erase(a[i]);
}
S.erase(tmp);
tL = L[tL] ;
tR = R[tR] ;
if( tL != 0 ){
R[tL] = tR;
}
if( tR != n+1 ){
L[tR] = tL;
}
}
for(int i=1;i<=n;i++){
printf("%d",ans[i]);
}
puts("");
return 0;
}