A. Min Or Sum
题意:给定一个数组,每次挑两个数换为任意与它们或运算结果相同的两个数。操作无限次,求数组最小和。
解:将它们全异或起来,换成这个数和一堆0。
答案:
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 1000005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int n,m; signed main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); int ans=0,t; for(int i=1;i<=n;i++){ scanf("%d",&t); ans|=t; } printf("%d\n",ans); } return 0; }View Code
B. Avoid Local Maximums
题意:给定一个数组,每次可以选择一个数换为任意数,求几次操作后可以消去所有局部最大值。
解:设局部最大由三个数i,i+1,i+2组成,i<i+1>i+2。考虑替换i+2,让它变大,这样后面产生局部最大值可能性变小。但也不能太大,挑i+1和i+3里较大的就可以了。(又名根据样例猜结论
代码:
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int n,m; int a[maxx]={0}; signed main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int ans=0; for(int i=2;i<n;i++){ if(a[i]>a[i+1]&&a[i]>a[i-1]){ ans++; a[i+1]=max(a[i],a[i+2]); } } printf("%d\n",ans); for(int i=1;i<=n;i++) printf("%d ",a[i]); printf("\n"); } return 0; }View Code
C. Differential Sorting
题意:给定一个数组,每次选择三个下标x<y<z,令a[x]=a[y]-a[z]。求一种方案使得数组不减。
解:构造题一般是挑中一个点死命用。显然a[n-1]<=a[n],否则无解。其次a[n]还得是换完以后的最大值,剩下所有元素必须小于a[n]和a[n-1]。由于位次关系只能将别的元素换为a[n-1]-a[n],这显然是一个负数。当a[n]>=0时,成立;a[n]<0时,a[n-1]-a[n]>a[n-1],不能这么做。如果a[n-2]需要被替代,则无解。以此类推,只有原数组有序时有解。
代码:
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int n,m; int a[maxx]={0}; signed main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); if(a[n]<a[n-1]){ printf("-1\n"); continue; } if(a[n]>=0){ printf("%d\n",n-2); for(int i=1;i<=n-2;i++) printf("%d %d %d\n",i,n-1,n); continue; } if(is_sorted(a+1,a+n+1)){ printf("0\n"); } else printf("-1\n"); } return 0; } //a[n]<0 a[n-1]<a[n]<0 //-7 6 -5 4 -4 -3View Code
D. Infinite Set
*这题用scanf("%lld");貌似会WA,得上cin。
题意:给定一个集合,可以由以下方法衍生出新的数:1.x=x*2+1; 2.x=x*4。 现给出p,求集合小于2p的数的数量。(1<=n,p<=2*105)
解:p这么大,又和2的幂有关,要按位算。两种操作一个生成奇数,一个生成偶数,没有交集,这很好。那么可以视作:左移两位,左移一位再令末尾为1。大胆猜测和具体的数无关,和与p+1(2的p次幂有p+1位)差了几位有关。令sum[i]为和p+1差i位时能生成的数的数量,随便打一打表,发现sum[i]=sum[i-1]+sum[i-2]+1。其实不是很理解那个+1。。。但问题不大。
除此之外注意去重,从大的数出发,看看能不能变成更小的数,反之会漏。还有注意不要大于2p的数。
代码:
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 1000000007 //#define int long long int n,p; ll a[maxx],sum[maxx]={0}; set<ll> s; signed main() { sum[1]=1,sum[2]=2; for(int i=3;i<=200000;i++){ sum[i]=(sum[i-1]+sum[i-2]+1)%mod; } cin>>n>>p; for(int i=1;i<=n;i++){ cin>>a[i]; s.insert(a[i]); } for(int i=1;i<=n;i++){ ll x=a[i]; while(x){ if(x%2==1) x/=2; else if(x%4==0) x/=4; else break; if(s.count(x)){ s.erase(a[i]); break; } } } ll ans=0; for(auto i:s){ int cnt=0; ll x=i; while(x){ x/=2; cnt++; } if(cnt>p) continue; ans=(ans+sum[p+1-cnt])%mod; } cout<<ans<<"\n"; return 0; }View Code