Codeforces Round #613 选讲

http://codeforces.com/contest/1285/problem/D

从高位往低位看,如果某一位上全是一样的数字,则直接看下一位;如果某位上有0有1,表明这一位答案上一定有个1,但具体那个X是要取0还是取1,取决于后面的位数,由此可将数字按这一位的01分成两组,递归地解决问题。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n;
 5 #define maxn 200011
 6 int a[maxn];
 7 
 8 int dfs(int x,int L,int R)
 9 {
10     if (x<0) return 0;
11     if (((a[R]>>x)&1)==0 || ((a[L]>>x)&1)==1) return dfs(x-1,L,R);
12     int mid=L; for (;mid<=R && ((a[mid]>>x)&1)==0;mid++);
13     return (1<<x)+min(dfs(x-1,L,mid-1),dfs(x-1,mid,R));
14 }
15 
16 int main()
17 {
18     scanf("%d",&n);
19     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
20     sort(a+1,a+1+n);
21     printf("%d\n",dfs(29,1,n));
22     return 0;
23 }
View Code

http://codeforces.com/contest/1285/problem/E

思路点在于找到“删除后会增加/减少的那些线段并后的区间”,即什么情况下,删除某个区间后,并后的区间与初始的并相比会变多/变少。变多:对一个坐标x,如果此时x为某个线段的起点,且x刚好被且只被某线段i覆盖,则删去i后区间数+1。变少:如果某条线段的左端点本来就是初始并的某个左端点,那么删去之后会比初始并少去一个区间(当然可能还有新增加的,这由前一种情况统计)。采用扫描线的方法完成,把一个线段拆成两个修改操作,按坐标排序后分别统计初始答案和以上两种情况。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int T,n,lq;
 5 #define maxn 400011
 6 struct SEG{int l,r;}a[maxn];
 7 struct QUES
 8 {
 9     int x,id,ty;
10     bool operator < (const QUES &b) const {return x<b.x || (x==b.x && ty>b.ty);}
11 }q[maxn<<1];
12 void addq(int x,int id,int ty) {q[++lq]=(QUES){x,id,ty};}
13 int diff[maxn];
14 set<int> s;
15 int main()
16 {
17     scanf("%d",&T);
18     while (T--)
19     {
20         scanf("%d",&n);
21         lq=0;
22         for (int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].r),addq(a[i].l,i,1),addq(a[i].r,i,-1);
23         sort(q+1,q+1+lq);
24         
25         memset(diff,0,sizeof(diff));
26         int cnt=0,init=0;
27         for (int i=1,j,k;i<=lq;)
28         {
29             bool flag=(cnt==0);
30             j=i;
31             while (j<=lq && q[j].x==q[i].x) j++;
32             k=i;
33             for (;q[k].ty==1 && k<j;k++) cnt++;
34             if (flag && cnt==1) diff[q[i].id]--;
35             for (;q[k].ty==-1 && k<j;k++) cnt--;
36             if (cnt==0) init++;
37             i=j;
38         }
39         
40         s.clear();
41         for (int i=1,j,k;i<=lq;)
42         {
43             int add=(s.size()==1?(*s.begin()):-1);
44             j=i;
45             while (j<=lq && q[j].x==q[i].x) j++;
46             k=i;
47             for (;q[k].ty==1 && k<j;k++) s.insert(q[k].id);
48             if (s.size()>1 && ~add) diff[add]++;
49             for (;q[k].ty==-1 && k<j;k++) s.erase(s.lower_bound(q[k].id));
50             i=j;
51         }
52         
53         int ans=0;
54         for (int i=1;i<=n;i++) ans=max(ans,init+diff[i]);
55         printf("%d\n",ans);
56     }
57     return 0;
58 }
View Code

http://codeforces.com/contest/1285/problem/F

枚举gcd,每次只考虑gcd的倍数(复杂度允许),将它们分别除以gcd后,在其中找乘积最大的互质的两个数。可以将它们从大到小排序入栈,每次一个数x入栈后,若监测到栈中存在与他互质的数就不停弹栈,弹到没有为止,最后弹出去的那个就是与x互质且最大的数。与x互质的数的数量即$\sum_{d|x} \mu(d) \times cnt_d$,其中$cnt_d$为$d$的倍数个数(可以考虑两数互质的情况然后推广可得此式)。每次可枚举x的约数计算(复杂度++)。总复杂度为每个数约数个数的平方之和。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define LL long long
 4 
 5 int n;
 6 #define maxn 200011
 7 #define MAXNUM 100000
 8 int a[maxn];
 9 bool cmp(int a,int b) {return a>b;}
10 
11 vector<int> Div[maxn],mul[maxn];
12 int miu[maxn],pri[maxn],lp=0; bool vis[maxn];
13 void makeprime(int n)
14 {
15     miu[1]=1;
16     for (int i=2;i<=n;i++)
17     {
18         if (!vis[i]) pri[++lp]=i,miu[i]=-1;
19         for (int j=1;j<=lp && 1ll*i*pri[j]<=n;j++)
20         {
21             vis[i*pri[j]]=1;
22             miu[i*pri[j]]=-miu[i];
23             if (i%pri[j]==0) {miu[i*pri[j]]=0; break;}
24         }
25     }
26 }
27 
28 int top=0,sta[maxn],cnt[maxn];
29 
30 int main()
31 {
32     scanf("%d",&n);
33     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
34     sort(a+1,a+1+n,cmp);
35     
36     for (int i=1;i<=MAXNUM;i++)
37         for (int j=i;j<=MAXNUM;j+=i)
38             Div[j].push_back(i);
39     
40     for (int i=1;i<=n;i++)
41         for (int j=0;j<Div[a[i]].size();j++)
42             mul[Div[a[i]][j]].push_back(a[i]/Div[a[i]][j]);
43     
44     makeprime(MAXNUM);
45     
46     LL ans=0;
47     for (int i=1;i<=MAXNUM;i++) if (mul[i].size()>0)
48     {
49         top=0;
50         for (int j=0,now,last;j<mul[i].size();j++)
51         {
52             last=0; now=mul[i][j];
53             while (1)
54             {
55                 int tot=0;
56                 for (int k=0;k<Div[now].size();k++) tot+=miu[Div[now][k]]*cnt[Div[now][k]];
57                 if (tot>0)
58                 {
59                     last=sta[top];
60                     for (int k=0;k<Div[last].size();k++) cnt[Div[last][k]]--;
61                     top--;
62                 }
63                 else
64                 {
65                     ans=max(ans,1ll*last*now*i);
66                     break;
67                 }
68             }
69             sta[++top]=now;
70             for (int k=0;k<Div[now].size();k++) cnt[Div[now][k]]++;
71         }
72         while (top)
73         {
74             int last=sta[top];
75             for (int k=0;k<Div[last].size();k++) cnt[Div[last][k]]--;
76             top--;
77         }
78     }
79     
80     printf("%lld\n",ans);
81     return 0;
82 }
View Code
posted @ 2020-01-15 18:04  Blue233333  阅读(...)  评论(...编辑  收藏