好久没写博客了,因为最近也没做到啥有价值的题目。昨天做了一场CF,感觉题目还不错。。
A题:水题不多说了。。
B题:
这道题目的本质就是求谁有可能在所有的时间段都在开会。
那么我们就可以假设一开始所有的人都在开会。
在已知的这段时间段内,我们可以判断出每个人在会议的时间段的长度。
然后我们再求出这段时间段内所有有人的时间长度。
如果某个人在会议的时间长度等于所有的人在会议的时间长度的话,那么说明这个人有可能是领导。
vec[i]:i人出现在会议的时间段。
hh[i]:所有的人出现在会议的时间段。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<string>
using namespace std;
#define LL long long
int maps[201];
#define maxn 110000
struct list
{
int s;
int x;
int leap;
}sig[maxn];
struct listt
{
int st;
int ed;
}tt,peo[maxn];
vector<listt>vec[maxn];
vector<listt>hh;
int f[maxn*3];
int cmp(struct listt a,struct listt b)
{
return a.st<b.st;
}
void dos()
{
memset(f,0,sizeof(f));
sort(hh.begin(),hh.end(),cmp);
int st,ed;
st=ed=-1;
int i,j;
for(i=0;i<hh.size();i++)
{
if(hh[i].st>st)st=hh[i].st;
if(hh[i].st<ed)st=ed+1;
if(hh[i].ed>ed)ed=hh[i].ed;
for(j=st;j<=ed;j++)f[j]=1;
}
}
int main()
{
int len,i,j;
int n,m;
char ch;
int x;
while(~scanf("%d%d%*c",&n,&m))
{
for(i=1;i<=n;i++)
{
vec[i].clear();
}
for(i=1;i<=m;i++)
{
scanf("%c %d%*c",&ch,&x);
if(ch=='+')sig[i].s=1;
else sig[i].s=0;
sig[i].x=x;
}
for(i=1;i<=n;i++)
{
peo[i].st=1;
peo[i].ed=-1;
}
hh.clear();
int pp[maxn];
for(i=1;i<=m;i++)
{
int a=sig[i].x;
if(sig[i].s==1)
{
peo[a].st=2*i;
}
else
{
peo[a].ed=2*i;
hh.push_back(peo[a]);
vec[a].push_back(peo[a]);
}
}
for(i=1;i<=n;i++)
{
if(peo[i].st==1)continue;
if(peo[i].st>peo[i].ed)
{
peo[i].ed=2*(m+1);
hh.push_back(peo[i]);
vec[i].push_back(peo[i]);
}
}
dos();
vector<int>ans;
ans.clear();
int res=0;
for(i=0;i<=m*2+10;i++)if(f[i])res++;
for(i=1;i<=n;i++)
{
if(vec[i].size()==0)ans.push_back(i);
else
{
int as=0;
for(j=0;j<vec[i].size();j++)
{
int st=vec[i][j].st;
int ed=vec[i][j].ed;
as+=ed-st+1;
}
if(as==res)ans.push_back(i);
}
}
cout<<ans.size()<<endl;
for(i=0;i<ans.size();i++)
{
cout<<ans[i];
if(i!=(ans.size()-1))cout<<" ";
else cout<<endl;
}
}
return 0;
}
C题:
说到C题,心里尤其的伤心。。。
学校的网昨天不知道怎么抽了,打开个网页都得2分钟。。。
假如我们把每个人都看成一个点。
如果某个人说x,y是嫌疑犯,我们就把x,y两点之间联线。
这样,问题就转化成让我们从图中取出两个点,使得这两个点连到的边的个数大于等于p;
问有几种取法。
我们可以先算出所有点的度。
枚举每个点,看有几个点的度加上这个点的度大于等于p;
可以二分找。
然后在删掉一些非法的点对。
1,两个点一样。即如果点x的度乘以2大于等于p的话,结果--;
2,两个点的度的和虽然大于等于p,但是这两个点有重边。
还有一些要注意的情况:
1,结果有可能超INT,注意用long long;
2,题目输入的边(x,y)有可能y>x;
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<string>
using namespace std;
#define LL long long
int maps[201];
#define maxn 1100000
struct list
{
int a;
int b;
}edge[maxn],p;
int cmp(struct list a,struct list b)
{
if(a.a!=b.a)return a.a<b.a;
return a.b<b.b;
}
LL f[maxn];
LL num[maxn];
LL n,m;
int sea(int x)
{
int l=1;
int r=n+1;
int mid=(l+r)/2;
while(l<r)
{
if(f[mid]<x)l=mid+1;
else r=mid;
mid=(l+r)/2;
}
return mid;
}
int main()
{
int len,i,j;
char ch;
int a,b;
while(~scanf("%d%d",&n,&m))
{
int x,y;
for(i=1;i<=n;i++)f[i]=num[i]=0;
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
a=min(x,y);
b=max(x,y);
edge[i].a=a;
edge[i].b=b;
f[a]++;
f[b]++;
num[a]++;
num[b]++;
}
sort(f+1,f+n+1);
sort(edge+1,edge+n+1,cmp);
LL ans;
ans=0;
for(i=1;i<=n;i++)
{
LL x=num[i];
LL y=m-num[i];
LL w=sea(y);
LL ns=n-w+1;
if(x>=y)ns--;
ans+=ns;
}
// cout<<"_-"<<ans<<endl;
ans=ans/2;
int leap=1;
p=edge[1];
for(i=2;i<=n+1;i++)
{
if(edge[i].a==edge[i-1].a&&edge[i].b==edge[i-1].b)leap++;
else
{
a=p.a;
b=p.b;
// cout<<num[a]<<"-"<<num[b]<<" "<<leap<<endl;
if(((num[a]+num[b]-leap)<m)&&((num[a]+num[b])>=m))ans--;
leap=1;
p=edge[i];
}
}
cout<<ans<<endl;
}
return 0;
}