ABCD1水题。
哎,两个小时就除了这四个题,出题太慢了......
D2:
题意:给你n个数,每次可以选任意一个数,让它除以等于2(向下取整),可以操作任意多次,问最少操作多少次能使这n个数中有k个相等的数。
思路:用vector存一下就行了。做题的时候光想dp了,实际上不是dp,就是个思维题。
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+10;
using namespace std;
int n,k,a[maxn];
vector<int> v[maxn];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
if(k==1)
{printf("0\n");return 0;}
for(int i=1;i<=n;i++)
{
int p=a[i],cnt=0;
while(p>=0)
{
v[p].push_back(cnt);
if(p==0)
break;
p/=2;
cnt++;
}
}
int ans=inf,anss;
for(int i=0;i<maxn;i++)
{
if(v[i].size()>=k)
{
sort(v[i].begin(),v[i].end());
anss=0;
for(int j=0;j<k;j++)
anss+=v[i][j];
ans=min(ans,anss);
}
}
printf("%d\n",ans);
return 0;
}
E:
题意:给你一个整数n,再给你两个长度均为2的字符串(均有abc三个字符构成),问:能否找到一个长度为3n的字符串满足abc三个字符的数目均为n,并且上面两个字符串都不是这个字符串的子串,输出这个字符串,如果不存在这样的字符串输出NO。
思路:实际上这题答案一定存在,做的时候我也猜到了,就是不知道答案那个字符串是啥。
就是这个答案可以由简单的字符变换出来,比如:abc可以变换成aabbcc,aaabbbccc,或者abcabc,abcabcabc,就这两个情况,对abc搞个全排列,然后把这两种变换都是一下,一定能找到结果。
具体=可以看这篇博客:https://www.cnblogs.com/wawcac-blog/p/11464774.html
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=3e5+10;
using namespace std;
int n;
char s1[3],s2[3];
int a[3],b[3];
int ans[maxn];
int cnt[4],p[4];
bool ok()
{
for(int i=1;i<=min(3*n-1,36);i++)
{
if(ans[i]==a[1]&&ans[i+1]==a[2]) return false;
if(ans[i]==b[1]&&ans[i+1]==b[2]) return false;
}
return true;
}
bool okk()
{
if(n<=12)
{
for(int i=1;i<=3*n-1;i++)
{
if(ans[i]==a[1]&&ans[i+1]==a[2]) return false;
if(ans[i]==b[1]&&ans[i+1]==b[2]) return false;
}
return true;
}
if(a[1]==a[2]) return false;
if(b[1]==b[2]) return false;
if(p[1]==a[1]&&p[2]==a[2]) return false;
if(p[2]==a[1]&&p[3]==a[2]) return false;
if(p[1]==b[1]&&p[2]==b[2]) return false;
if(p[2]==b[1]&&p[3]==b[2]) return false;
return true;
}
void print()
{
puts("YES");
for(int i=1;i<=3*n;i++)
{
if(ans[i]==1) printf("a");
if(ans[i]==2) printf("b");
if(ans[i]==3) printf("c");
}
puts("");return ;
}
int main()
{
scanf("%d",&n);
scanf("%s%s",s1+1,s2+1);
a[1]=s1[1]-'a'+1;
a[2]=s1[2]-'a'+1;
b[1]=s2[1]-'a'+1;
b[2]=s2[2]-'a'+1;
p[1]=1;p[2]=2;p[3]=3;
do
{
for(int i=1;i<=n;i++)
ans[i]=p[1];
for(int i=1;i<=n;i++)
ans[i+n]=p[2];
for(int i=1;i<=n;i++)
ans[i+2*n]=p[3];
if(okk())
{print();return 0;}
for(int i=0;i<=n-1;i++)
ans[1+3*i]=p[1],ans[2+3*i]=p[2],ans[3+3*i]=p[3];
if(ok())
{print();return 0;}
}while(next_permutation(p+1,p+3+1));
puts("NO");
return 0;
}
G:
题意:一个有向图,每条边都有一个权值,有m个询问,每个询问找出有多少对点使得这些点对之间的最大边权不大于q。
思路:
把询问离线,对于每个询问,我们把比他的要求小的边全部加进树中。
那么答案就是所有连通块的贡献,每个连通块的贡献是sz*(sz-1)/2
那么我们按询问的要求值从小到大排序,然后每次尝试加边,并用并查集维护连通块大小
参考博客:https://blog.csdn.net/liufengwei1/article/details/100168298
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+10;
using namespace std;
struct node{
int u,v,edge;
}a[maxn];
struct nodee{
int q,id;
ll anss;
}ans[maxn];
int n,m,fa[maxn];
ll Size[maxn];
bool cmp1(node a1,node a2) {return a1.edge<a2.edge;}
bool cmp2(nodee a1,nodee a2) {return a1.q<a2.q;}
bool cmp3(nodee a1,nodee a2) {return a1.id<a2.id;}
int findfa(int x) {return fa[x]==x?x:fa[x]=findfa(fa[x]);}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;i++)
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].edge);
sort(a+1,a+n,cmp1);
for(int i=1;i<=m;i++)
scanf("%d",&ans[i].q),ans[i].id=i;
sort(ans+1,ans+m+1,cmp2);
for(int i=1;i<=n;i++)
fa[i]=i,Size[i]=1;
int p=1;
ans[1].anss=0;
for(int i=1;i<=m;i++)
{
ll tmp=ans[i-1].anss;
ans[i].anss=tmp;
while(a[p].edge<=ans[i].q&&p<=n-1)
{
int uu=a[p].u,vv=a[p].v;
int fau=findfa(uu),fav=findfa(vv);
if(fau!=fav)
{
fa[fau]=fav;
tmp-=Size[fau]*(Size[fau]-1)/2;
tmp-=Size[fav]*(Size[fav]-1)/2;
Size[fav]+=Size[fau],Size[fau]=0;
tmp+=Size[fav]*(Size[fav]-1)/2;
ans[i].anss=tmp;
}
p++;
}
}
sort(ans+1,ans+m+1,cmp3);
printf("%I64d",ans[1].anss);
for(int i=2;i<=m;i++)
printf(" %I64d",ans[i].anss);
return 0;
}