Halloumi Boxes
题目大意:有一个数组a[],每次可选定一段长为k的区间,将区间内的数反转,问最后能不能使a[]按照非降序排列。
思路:不要想复杂了,只要这个k>=2那么就可以两两交换,那不就是冒泡排序嘛,直接就成立了。但是如果k是1,而且还有逆序的,肯定不可以,k=1,换了等于没换。
#include
using namespace std;
int a[200010];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int c=a[1];
int flag=1;
for(int i=2;i<=n;i++)
{
if(c>a[i])
{
flag =0;
break;
}
c=a[i];
}
if(!flag&&k==1) printf("NO\n");
else printf("YES\n");
}
}
StORage room
题目大意:我们给定一个n*n的表M,要求一个大小为n的数组a[],使得Mij=a[i]|a[j](i!=j),如果数组不存在,输出“NO”;否则输出“YES”,并输出a[].
思路:既然涉及到位运算,那么我们就从二进制的角度来看。|的意义就是两个数中的当前位,只要有一个是1,那么结果的这一位就取1,我们每个数都与剩下的数进行了该运算,我们看这个数x与其他数进行或运算后产生的01串,可以发现,有些位上对于所有的串来说都是1,那么显然,x的这一位也应该是1,所以x的计算只用将这些数取&(都是1才为1)即可。为了保证初值不影响后续计算,我们将初值赋为1073741823(二进制下30个1,Mij的范围是小于2^30)。最后要考虑到不成立的情况,那么就将得到的数组去计算一遍,看能否得到M即可。
#include
using namespace std;
#define int long long
int m[1010][1010];
int a[1010];
signed main()
{
int t;
scanf("%lld",&t);
while(t--)
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
a[i]=1073741823;
for(int j=1;j<=n;j++)
scanf("%lld",&m[i][j]);
}
if(n==1) printf("YES\n"),printf("1\n");//任意一个数都可以
else if(n==2) //恒成立的
{
printf("YES\n");
printf("0 %lld\n",m[1][2]);
}
else
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j) a[i] &= m[i][j];
int flag=1;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if((a[i]|a[j])!=m[i][j])
{
flag=0;
break;
}
}
if(!flag) break;
}
if(flag)
{
printf("YES\n");
for(int i=1;i<=n;i++) printf("%lld ",a[i]);
printf("\n");
}
else
{
printf("NO\n");
}
}
}
}
Theofanis' Nightmare
题目大意:给定一个数组a[],我们可以将它化成若干个子数组,子数组之间没有重叠。我们给子数组按顺序标上序号,sumi作为第i个子数组的和,ans=sum(sumi*i),求ans的最大值。
思路:这道题是贪心题,贪心的思路特别妙。我们来看从一个位置划开意味这什么,意味着后面所有的数都要被多加一次,那么如果我们知道了所有数的和(后缀和),那么这个和如果大于0,对于结果的贡献肯定是正的,如果小于0,会对结果产生负面影响,自然不能从这个位置划开,首先,大家都避免不了要被算一次,剩下的是否会再被算就要看我们的后缀和了。所以,这个贪心的思路真的很妙,去考虑划分产生的意义。
#include
using namespace std;
#define int long long
int a[200010],s[200010];
signed main()
{
int t;
scanf("%lld",&t);
while(t--)
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
s[n]=a[n];
for(int i=n-1;i>=1;i--) s[i]=s[i+1]+a[i];
int ans=s[1];
for(int i=2;i<=n;i++)
{
if(s[i]>0) ans += s[i];
}
printf("%lld\n",ans);
}
}
Maximum And Queries (easy version)
题目大意:现有一个大小为n的数组a[],我们最多可以进行k次操作,每次操作可以将一个a[i]加1,最后需要求出a[]的&,问最大结果是多少。
思路:这道题既然是位运算,那么我们就从二进制的角度来看,当所有数的某一位都是1的时候,那么结果的这一位就是1,我们要做的就是在k次操作中尽可能地使高位为1,那么其实可以直接模拟,从高位开始看,遍历每一位,假设要使当前这个数地这一位变成1,需要操作多少次,把次数累加起来,如果次数超过k,那么自然不可以,如果次数小于k,那么可以去看看后面的位还有没有能够变成1的。剩下的还有一个点就是来确定,当前这个数的这一位如果变成1,最少需要加多少。
我们以第j位为例,判断第j位是否是1(从后往前依次是第0位,第1位,......),a[i]&(1< 7:111 所以a[i]%(1< 至此,讨论清楚,问题解决。另外还要注意一点,如果当前位可以进行操作,需要实际去操作,否则会导致后面的统计出问题。还有如果是1<
ps:不管什么时候遇到位运算,一定要从二进制的角度去考虑,这样会大大优化题目思路。
4:100
7%4==3:11
8:1000
8%4==0:00
9:1001:
9%4==1:01#include