A. Plus One on the Subset
分析:
由于一次可以选中任意多个元素加一;所以本题其实要求的就是序列中最大值和最小值的差。
AC代码:
#includeusing namespace std; #define INF 0x7fffffff int main(){ int t;cin>>t; while(t--){ int n,minn=INF,maxx=-INF; cin>>n; for(int i=1;i<=n;i++){ int x;cin>>x; minn=min(minn,x); maxx=max(maxx,x); } cout<
B. Make AP
分析:
假设我们开始输入的是a,b,c;最后选中三个数中任意一个数乘于正整数m(注意m只能是正整数),使最后的a’,b’,c’满足等差序列的性质即a’+c’==2*b’。由于a、b或c都有可能需要乘于m,所以显然有三种情况:
1、a * m +c ==2* b
2、a + c == 2* b * m
3、a +c * m == 2* b
分别对三种结果做处理,则会出现以下三种判别方式(除法结果大于0是为了保证m是正整数):
1.(2 * b-c)%a ==0&&(2b-c)/a>0
2.(a+c)% (2b) ==0&&(a+c)/(2b)>0
3.(2 * b-a)%c ==0&&(2b-a)/c>0AC代码:
#includeusing namespace std; #define INF 0x7fffffff int main(){ int t;cin>>t; while(t--){ int a,b,c; cin>>a>>b>>c; int m=2*b-c,p=2*b-a,flag=0; if(m/a>0&&m%a==0||p/c>0&&p%c==0||(a+c)/(2*b)>0&&(a+c)%(2*b)==0) cout<<"YES"<
C. Division by Two and Permutation
分析:
由于n<=50,我们可以考虑一下怎么样才能暴力遍历1…n就能得到结果呢? 我们可以先预处理1…n之间的数据i,找到每一个i可能得到的值x(1<=x<=n),并将记录x的值加一,即cnt[x]++;预处理结束后,我们便得到了长度为n的序列要想在一系列处理之后变成序列1…n,那么其中每一个数i的最小出现次数为cnt[i]。然后对我们输入的序列进行处理,同样记录他通过处理可能得到的1…n之间的数的个数,将其结果记录在vector容器g[1…n]中;最后遍历一遍1~n,如果至少出现过一次g[i].size()时间复杂度为:O(nlog2n) AC代码:
#includeusing namespace std; #define INF 0x7fffffff int main(){ int t;cin>>t; while(t--){ vector g[100]; int cnt[100],n; cin>>n; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++){ int x=i; while(x>=1){ cnt[x]++; x/=2; } } for(int i=1;i<=n;i++){ int x;cin>>x; while(x>=1){ if(x<=n) g[x].push_back(i); x/=2; } } bool flag=true; for(int i=1;i<=n;i++){ if(g[i].size()
D. Palindromes Coloring
分析:
刚开始写蒻蒻写这道题的时候毫无思路,并且还有点烦躁,但是后面一看提交,发现有三千多个提交了,我就稳定心来决定要做出这道题。当然,我也很快就AC掉了。那我们一起来分析一下吧!!!
本题题意是将通过k种颜色将长度为n的字符串分成k堆,每一堆之间的字符可以随意调换位置任意多次,并且不一定每一个字符都需要被涂上颜色(这是一个很好的条件,将题目简化了许多),让我们求出这几堆中最短的回文字符串的最大长度。看到这,估计很多小伙伴跟我开始写之前一样,还是挺懵的。别着急,我们慢慢来看。
首先我们可以明确一个要求,要让最短的回文字符串的长度最大,那么我们需要做的就是将回文字符串尽量平均分给每一个堆。我们可以很容易求出n/k是平均分成k堆以后最短的字符串的最大长度;因为被染颜色的字符个数是<=n的。接下来就是将回文串进行均分了。我们知道,两个相同的字符一定是回文串,我们记它为一对;所以我们可以遍历一遍字符串找到字符串中所有的对数ou,(注意:aaaa相当于是两对),最后k堆中的每一堆均分到的对数为ou/k,这些字符长度为ou / k* 2 ; 最后一个细节就是,只有一个字符或者有(x对和一个字符)都是回文串;所以最后的结果就是min(n/k,ou /k * 2+1)。AC代码:
#includeusing namespace std; #define INF 0x7fffffff int main(){ int cnt[30],t;cin>>t; while(t--){ int n,k; cin>>n>>k; int ans=n/k; int ou=0,res; memset(cnt,0,sizeof(cnt)); string str; cin>>str; sort(str.begin(),str.end()); for(int i=0;i 接下来几道就是蒻蒻看完大佬思路以后的补题了,蒻蒻还是太菜了,以后还需要更加努力!!!
G. MinOr Tree
分析: 一道思维+暴力的题目。
总结规律吧:我发现只要是位运算的题目求最值,解法绝大部分都是从高位到低位暴力处理每一位。
拿本题为例,本题需要求出一颗边权值做位或以后值最小的生成树。也就是位运算的最值问题。我们可以从高位到低位开始遍历每一位,如果边权值没有该位的所有边能构成一棵生成树,那么该位无效,可以标记一下所有边权值带该位的边为已访问,这样接下来就不用考虑这些边了;否则如果边权值没有该位的所有边不能构成一棵生成树,说明最后的答案里一定包含该位,所以可以在最后结果里直接加上该位。最后一个问题了,那么如何判断是否能构成生成树呢?我们可以用并查集的方法,把所有未标记并且边权值不包括该位的边合并在一起,最后看看所有的结点1~n是否在同一个连通图里,如果在,说明可以构成生成树,否则不可以。
时间复杂度:O(30*(2*m+n)),也就是O(m+n)。#includeusing namespace std; #define INF 0x7fffffff const int maxn=2e5+100; struct Edge{ int from,to,w; }g[maxn]; int n,m,fa[maxn]; bool vis[maxn]; inline int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } inline void Union(int u,int v){ if(find(u)==find(v)) return ; fa[find(u)]=find(v); } void Init(){ for(int i=1;i<=n;i++) fa[i]=i; } bool check(int x){ Init(); for(int i=1;i<=m;i++){ if(vis[i]||g[i].w&(1< >n>>m; for(int i=1;i<=m;i++){ vis[i]=false; cin>>g[i].from>>g[i].to>>g[i].w; } int res=0; for(int i=30;i>=0;i--){ //没有这条边可以构成生成树 if(check(i)){ for(int j=1;j<=m;j++) if(g[j].w&(1<>t; while(t--){ cout< 欢迎分享,转载请注明来源:内存溢出
评论列表(0条)