9.18题解

T1

我并没有发现规律,或者说我本来有向那个方向想,但是我觉得不对,就否定了我自己,而事实上他是真的可以证出来的,而且是什么鸽巢原理,我颓了一下百度,然而并不是太会,接下来口胡一下这道题的证明

有$n$个数,分别为$a_1$,$a_2$,$a_3$,${\cdots}$,$a_n$,那么一定可以得到$n$个不同的前缀和$a_1$,$a_1+a_2$,$a_1+a_2+a_3$,${\cdots}$,$a_1+a_2+a_3+{\cdots}+a_n$,在${\%}n$意义下余数一定分布在$[0,n)$,那么我们分两种情况来讨论

1.这$n$个数的余数均不相同,那么一定有一个的余数是$0$,也就是整除了

2.这$n$个数中有的余数相同,相同了,那这两个前缀和的区间和在${\%}n$意义下就是$0$,依旧被整除了

综上原问题得证,那么连续的区间就一定可以构造出来答案,直接看哪两个的前缀和相同,直接输出答案即可

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 1001000
 4 #define enter puts("") 
 5 #define space putchar(' ')
 6 using namespace std;
 7 int n,flag,qzh;
 8 int a[maxn];
 9 inline int read()
10 {
11     int e=0;  char ch=getchar();
12     while(ch<'0'||ch>'9')  ch=getchar();
13     while(ch>='0'&&ch<='9')  {e=(e<<3)+(e<<1)+(ch^48);  ch=getchar();}
14     return e;
15 }
16 inline void write(int x)
17 {
18     if(x<0)  {putchar('-');  x=~(x-1);}
19     int s[20],top=0;
20     while(x)  {s[++top]=x%10;  x/=10;}
21     if(!top)  s[++top]=0;
22     while(top)  putchar(s[top--]+'0');
23 }
24 int main()
25 {
26     //freopen("1.in","r",stdin);
27     n=read();  a[0]=0;
28     for(int i=1;i<=n;++i)
29     {
30         int x=read();  x%=n;
31         if(flag)  continue;
32         qzh=(qzh+x)%n;
33         if(a[qzh])
34         {
35             write(i-a[qzh]);  enter;
36             for(int j=a[qzh]+1;j<=i;++j)  {write(j);  space;}
37             enter;  flag=1;
38         }
39         else  a[qzh]=i;
40     }
41     if(!flag)  {write(-1);  enter;}
42     return 0;
43 }
稍卡内存

 

T2

人生第一次见到一道题内存给$16MB$,当然随后我就见到了内存多达$8MB$的题,看到题的时候整个人处于癫狂的状态,关键是当时我算了算,似乎是之够把读入存下来,然后就什么都不能干了,关键是我理解错了题意,还觉得自己做的非常正确,还卡了过去,然后事实证明,我只有对读入的理解是正确的,这道题的意思是你先选,选完之后再排列就可以了,你先选的可以后看,那么问题就变成了,我选的每一种书不超过我选的所有书的总数的一半,最多$+1$,只要不超过一半,你就可以在选择次数最多的书中间插入其他的书把他们隔开,总能构造出一种合法的看书顺序,那么基于贪心,对于所有的书,我最多只会有一种书超过总数的一半,也就是除了出现次数最多的书之外,其余的书我都可以全选,这样的话,如果某一种书超过了总书数的一半,我就选其余书总数那么多,或者加一,插进去,一定是最好的情况,如果出现次数最多的那种书都没有超过总书数的一半,我就可以全选进来,合法解一定可以被构造出来

那么问题就变成了找到出现次数最多的书,并计算他的数量,由于空间卡的很紧,我们显然不能存下来所有书的种类,经过刚才的分析可知,如果出现次数最多的那本书没超过一半,那就没有影响,所以我们用一个变量$sum$来判断某一种书有没有超过一半,用$id$记录书的种类,如果对于一本书,扫到他的时候$sum=0$,那么就让$sum++$,$id$记为这本书的种类,如果扫到这本书时$sum!=0$,如果他的种类和当前$id$种类相同,就让$sum++$,否则$sum--$,那相当与我们就是在进行一个平衡的过程,最后记录下来的$id$,要么是出现次数大于一半的书的种类,因为大于一半无法被均衡掉,如果没有出现次数大于一半的书,那么就是在最后出现几种书的$id$中的一种,其实是谁并不重要,然后我们再重新扫所有书的种类,如果种类为最后的$id$,就$tot++$,这样我们就可以统计出如果有出现次数大于一半的,他的次数是多少,如果最后的$id$小于总书数的一半,证明那个$id$就是拿来凑数的,否则按照我们之前说的,确定出现次数多于一半的这本书需要有几本不选即可

注意确定本数的时候用的是除了这本以外的其他种书的总种数,而不是总书数的一半,因为这种书只能插其他种书的空,不能插自己的空

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxm 1010
 4 #define ll long long
 5 using namespace std;
 6 int m,k,cnt,n;
 7 ll id;
 8 ll a;
 9 int count[maxm],x[maxm],y[maxm],z[maxm];
10 int main()
11 {
12     scanf("%d%d",&m,&k);
13     for(int i=1;i<=m;++i)  {scanf("%d",&count[i]);  n+=count[i];}
14     for(int i=1;i<=m;++i)  scanf("%d",&x[i]);
15     for(int i=1;i<=m;++i)  scanf("%d",&y[i]);
16     for(int i=1;i<=m;++i)  scanf("%d",&z[i]);
17     int s=(1<<k)-1;
18     for(int i=1;i<=m;++i)
19     {
20         a=x[i];
21         if(!cnt)  {id=a;  cnt=1;}
22         else if(a==id)  cnt++;
23         else  cnt--;
24         for (int j=1;j<count[i];++j)
25         {
26             a=(a*y[i]+z[i])&s;
27             if(!cnt)  {id=a;  cnt=1;}
28             else if(a==id)  cnt++;
29             else  cnt--;
30         }
31     }
32     cnt=0;
33     for(int i=1;i<=m;++i)
34     {
35         a=x[i];
36         if(a==id)  cnt++;
37         for (int j=1;j<count[i];++j)
38         {
39             a=(a*y[i]+z[i])&s;
40             if(a==id)  cnt++;
41         }
42     }
43     if(cnt>n-cnt+1)  printf("%d\n",cnt-n+cnt-1);
44     else  printf("0\n");
45     return 0;
46 }
疯狂卡空间

T3

 神奇的对平方的转化,考虑一个数学问题,什么时候平方最常用?算正方形面积,那么$x^2$其实就是计算一个边长为$x$的正方形面积,那考虑我们是不是可以找到到底是谁给这个正方形的面积作出了贡献,我们枚举两个人,这两个人对答案的贡献其实就是他们两个同时在x前面的天数

突如其来的咕咕咕,所以扔代码和题解了

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 200200
 4 #define ll long long
 5 #define mod 1000000007
 6 using namespace std;
 7 int n,m,tot=1,ans,base;
 8 int a[maxn],f[35],sum[maxn*30];
 9 int trie[maxn*30][2];
10 void add(int x)
11 {
12     int u=1;
13     for(int k=m-1;k>=0;--k)
14     {
15         int c=(x>>k)&1;
16         if(!trie[u][c])  trie[u][c]=++tot;
17         sum[trie[u][c]]++;  u=trie[u][c];
18     }
19 }
20 void find(int x)
21 {
22     int u=1;
23     for(int k=m-1;k>=0;--k)
24     {
25         int c=(x>>k)&1;
26         f[k]=sum[trie[u][c^1]];
27         u=trie[u][c];
28     }
29 }
30 int main()
31 {
32     scanf("%d%d",&n,&m);  base=1<<(m-2);  base=base%mod;
33     for(int i=1;i<=n;++i)  {scanf("%d",&a[i]);  add(a[i]);}
34     for(int i=1;i<=n;++i)
35     {
36         ll da=0;  find(a[i]);
37         for(int j=0;j<m;++j)
38             for(int k=j;k<m;++k)  da=(da+(2*(1ll*f[j]*f[k])%mod)%mod)%mod;
39         da=da%mod;  da=(da*base)%mod;  ans=ans^da;
40     }
41     printf("%d\n",ans);
42     return 0;
43 }
View Code

 

posted @ 2019-10-11 06:25  hzoi_X&R  阅读(147)  评论(0编辑  收藏  举报