题意:给你一个数n,你要找出是否存在k个 2^i 这样的数,使得这k个数的和是n。
思路:水题,如果n换成二进制1的个数小于k,那么可以把高一位的数换成2*低一位的数,比如1000换成两个100即可。
#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
map<int,int>::iterator it;
int ss(int x)
{
int res=0,n=1;
while(x)
{
if(x&1)
{
mp[n]=1;
res++;
}
n=n*2;
x/=2;
}
return res;
}
int main()
{
int n,k;
cin>>n>>k;
if(k>n||ss(n)>k)puts("NO");
else
{
puts("YES");
int res=ss(n);
for(int i=30;i>0;i--)
{
if(res==k)break;
int x=(int)pow(2,i);
int y=x/2;
int t=mp[x];
if(!t)continue;
if(k-res>t)
mp[x]=0,mp[y]+=2*t,res+=t;
else
{
int cha=k-res;
mp[x]-=cha;
mp[y]+=2*cha;
res=k;
}
}
for(it=mp.begin();it!=mp.end();it++)
{
int x=it->first;
int y=it->second;
for(int i=1;i<=y;i++)
printf("%d ",x);
}
}
}
未理解D. Circular Dance
题意:有n个人连接一个单向环,给出第 i 个人的下一个人和下下个人的编号(有可能是反的),求出这n个人构成的环。
思路:可以直接从1开始推,如果1的后面有3和5,那么再分别看3和5,如果3的后面有5,1的后面可能是3,如果5的后面有3,1的后面就可能是5。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+11;
int p[maxn],a[maxn][2],vis[maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i][0],&a[i][1]);
int i=1,res=1;
printf("1");
vis[1]=1;
while(res<n)
{
int x=a[i][0],y=a[i][1];
if((a[x][0]==y||a[x][1]==y)&&!vis[x])
p[i]=x;
else
p[i]=y;
i=p[i];
vis[i]=1;
printf(" %d",i);
res++;
}
}
未理解E. Almost Regular Bracket Sequence
题意:给你一个长度为n的括号串,你可以独立的选择一个位置,改变那个位置的括号之后,如果总括号串是平衡的,这就是一个合法位置,求一共有多少个合法的位置。
思路::貌似正解是o(n)算法,我暂时还不会,先撸一个线段树解法吧,设两颗线段树tl,tr分别表示区间 ‘)’ 的个数和区间’('个数。那么怎么进行区间合并呢,假设左儿子为:))(((,右儿子为:))(((,合并消除()之后就变成了))((((,所以我可以从1枚举到n,在线段树中改变第 i 个位置的括号,如果线段树根节点的tl=tr=0,那么答案就++。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
char s[maxn];
int tl[maxn*4],tr[maxn*4];
void push(int o,int ls,int rs)
{
if(tr[ls]>=tl[rs])
{
tl[o]=tl[ls];
tr[o]=tr[rs]+tr[ls]-tl[rs];
}
else
{
tr[o]=tr[rs];
tl[o]=tl[ls]+tl[rs]-tr[ls];
}
}
void build(int o,int l,int r)
{
if(l==r)
{
if(s[l]=='(')
tr[o]=1;
else tl[o]=1;
return;
}
int m=(l+r)/2,ls=o*2,rs=o*2+1;
build(ls,l,m);
build(rs,m+1,r);
push(o,ls,rs);
}
void up(int o,int l,int r,int k)
{
if(l==r)
{
tl[o]=!tl[o];
tr[o]=!tr[o];
return;
}
int m=(l+r)/2,ls=o*2,rs=o*2+1;
if(k<=m)up(ls,l,m,k);
else up(rs,m+1,r,k);
push(o,ls,rs);
}
int main()
{
int n,ans=0;
cin>>n;
scanf("%s",s+1);
build(1,1,n);
for(int i=1;i<=n;i++)
{
up(1,1,n,i);
if(tl[1]==0&&tr[1]==0)ans++;
up(1,1,n,i);
}
cout<<ans;
}
未理解F. Make It Connected
题意:给你m条带权值的边,和n个有权值的点,你可以使用这些边,也可以不使用而直接连接u v,权值为ax+ay,求最小生成树。
思路:找出最小的ap,然后加n-1条 p 到 i 的权值为ap+ai的边,再加上那m条边,然后再用kruskal算法求最小生成树即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll v[maxn],mn=1e15;
int n,m,p,res,par[maxn];
struct node
{
int x,y;
ll w;
bool operator<(const node&t)const
{
return w<t.w;
}
}a[maxn*2];
int find(int x)
{
if(par[x]!=x)
par[x]=find(par[x]);
return par[x];
}
ll kruskal()
{
sort(a+1,a+1+m);
ll ans=0;
for(int i=1;i<=m;i++)
{
int fx=find(a[i].x),fy=find(a[i].y);
if(fx==fy)continue;
ans+=a[i].w;
par[fx]=fy;
if(++res==n-1)return ans;
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
par[i]=i;
scanf("%lld",&v[i]);
if(mn>v[i])
mn=v[i],p=i;
}
for(int i=1;i<=m;i++)
scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].w);
for(int i=1;i<=n;i++)
{
if(i==p)continue;
++m;
a[m].x=p,a[m].y=i,a[m].w=v[p]+v[i];
}
cout<<kruskal();
}