比赛链接:https://codeforces.com/contest/1249
给你n个数,问你能不能分成最少的组,使得组内任意两数之差>1。
先排序,如果存在排名相近的两个数之差<=1,则分成两组,否则一组。
#include
#include
#include
#include
#include
using namespace std;
int a[105],f[105];
int main(){
int q;
cin>>q;
while(q--){
memset(f,0,sizeof(f));
int n,flag=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i=2;i<=n;i++){
if(a[i]-a[i-1]==1){
flag=1;
cout<<2<<endl;
break;
}
}
if(!flag)
cout<<1<<endl;
}
return 0;
}
有 i i i个人,第 i i i个人会在下一天把书给第 a i a_{i} ai,问每个人在第几天拿到自己的书, n < = 200 n<=200 n<=200。
比如有 a = [ 5 , 1 , 2 , 4 , 3 ] a=[5,1,2,4,3] a=[5,1,2,4,3]
第一个人的书会进行如下传递:
第一天:传到 5 5 5手中
第二天:传到 3 3 3手中
第三天:传到 2 2 2手中
第四天:到 1 1 1自己手中
答案就是 4 4 4
#include
#include
#include
#include
#include
using namespace std;
int a[205],f[205];
int main(){
int q,n;
cin>>q;
while(q--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
int t=i,ans=1;
while(a[t]!=i){
t=a[t];
ans++;
}
cout<<ans<<' ';
}
cout<<endl;
}
return 0;
}
同上题, n < = 2 ⋅ 1 0 5 n<=2·10^5 n<=2⋅105
求每个人所在强连通分量的大小,我用的 T a r j a n Tarjan Tarjan。
#include
#include
#include
#define N 200010
using namespace std;
int low[N],dfn[N],sta[N],belong[N],a[N],num[N],index,scc,top,n;
bool insta[N];
void tarjan(int u){
int v;
low[u]=dfn[u]=++index;
sta[top++]=u;
insta[u]=true;
v=a[u];
if(!dfn[v]){
tarjan(v);
if(low[u]>low[v])
low[u]=low[v];
}
else if(insta[v] && low[u]>dfn[v])
low[u]=dfn[v];
if(low[u]==dfn[u]){
scc++;
do{
v=sta[--top];
insta[v]=false;
belong[v]=scc;
num[scc]++;
}
while(v!=u);
}
}
int main(){
int q;
cin>>q;
while(q--){
index=scc=top=0;
memset(dfn,0,sizeof(dfn));
memset(num,0,sizeof(num));
memset(insta,0,sizeof(insta));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;i++)
cout<<num[belong[i]]<<' ';
cout<<endl;
}
return 0;
}
定义一个Good Number为这个数由且仅由 3 3 3的不同幂的和组成(不可重复)。
如: 30 = 3 3 + 3 1 30=3^3+3^1 30=33+31是Good Number, 19 = 3 2 + 3 3 + 3 0 = 3 2 + 3 1 + 3 1 + 3 1 + 3 0 19=3^2+3^3+3^0=3^2+3^1+3^1+3^1+3^0 19=32+33+30=32+31+31+31+30不是Good Number
要求找出第一个 > = n >=n >=n的Good Number, 1 < = n < = 1 0 4 1<=n<=10^4 1<=n<=104。
打表找递推公式。
#include
#include
#include
#include
#include
using namespace std;
int ans[20000],num;
int main(){
int q,n;
ans[1]=1;
for(int i=1;i<=20000;i++){
ans[i]=(i%2==0?ans[i/2]*3:ans[i-1]+1);
}
cin>>q;
while(q--){
int n;
cin>>n;
for(int i=1;i<=num;i++)
if(ans[i]>=n){
cout<<ans[i]<<endl;
break;
}
}
return 0;
}
如上,但 n n n的范围为 1 < = n < = 1 0 18 。 1<=n<=10^{18}。 1<=n<=1018。
上题中ans[i]=(i%2==0?ans[i/2]*3:ans[i-1]+1);
根据上式发现当 i = 2 x i=2^x i=2x时, a [ i ] = 3 x a[i]=3^x a[i]=3x, i i i为奇数时, a [ i ] = a [ i − 1 ] a[i]=a[i-1] a[i]=a[i−1],则存在这样一条性质。即 a [ i ] = a [ i − l o w b i t ( i ) ] + a [ l o w b i t ( i ) ] a[i]=a[i-lowbit(i)]+a[lowbit(i)] a[i]=a[i−lowbit(i)]+a[lowbit(i)],则可以用 l o g n log{n} logn的时间求 a [ i ] a[i] a[i],通过二分法查找第一个 > = n >=n >=n的 i i i,就可以解决此题。
时间复杂度 ( l o g n ) 2 {(log{n}})^2 (logn)2
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline ll qpow(ll a, ll n)
{
ll ans = 1;
while(n)
{
if(n & 1) ans = ans * a;
a = a * a;
n >>= 1;
}
return ans;
}
inline ll lowb(ll x){
return x&-x;
}
inline ll get2(ll x){
ll t=-1;
while(x){
x>>=1;
t++;
}
return t;
}
inline ll f(ll x){
if(x==1)
return 1;
if(lowb(x)==x){
return qpow(3,get2(x));
}
return f(x-lowb(x))+f(lowb(x));
}
ll bserch(ll l,ll r,ll v){
while(l<=r){
ll mid=(l+r)/2;
ll p=f(mid);
if(p<v)
l=mid+1;
else if(p>v)
r=mid-1;
else {
return mid;
}
}
return l;
}
int main(){
ll n,pos;
int q;
cin>>q;
while(q--){
scanf("%lld",&n);
pos=bserch(1,min(n,274877906944),n);//打表找出1^18对应的i
cout<<f(pos)<<endl;
}
return 0;
}
删除最少的线段,使得坐标轴上每个点被线段覆盖的次数<=k
贪心地对于点 p p p,如果覆盖 p p p的线段 > k >k >k个,则删除覆盖p且右端点最大的线段。
用set维护覆盖点 i i i的线段,做法是随时弹出 S S S内右端点 < i <i的线段,并加入以 i i i为左端点的线段。
#include
#include
#include
#include
#define N 200005
#define pii pair
using namespace std;
vector<pii> pnt[N];
vector<int>ans;
set<pii> s;
int main(){
int n,k,R=0,L=0x3f3f3f,l,r;
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%d %d",&l,&r);
pnt[l].push_back(make_pair(r,i));
R=max(R,r);
L=min(L,l);
}
for(int i=L;i<=R;i++){
while(!s.empty() && s.begin()->first<i)
s.erase(s.begin());
for(int j=0;j<pnt[i].size();j++)
s.insert(pnt[i][j]);
while(s.size()>k){
ans.push_back(prev(s.end())->second);
s.erase(prev(s.end()));
}
}
cout<<ans.size()<<endl;
for(int i=0;i<ans.size();i++)
cout<<ans[i]<<' ';
return 0;
}