A. Mezo Playing Zoma (CF 1285 A)
題目大意
機器人初始位置為\(0\),給了一串指令告訴機器人是往左走一格還是右走一格,部分指令可能會丟失,問機器人最終可能的位置的情況數。
解題思路
\(L,R\)的個數加一即為答案。
神奇的代碼
#include <bits/stdc++.h>
using namespace std;
int main(void) {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
string s;
cin>>n>>s;
int l=0,r=0;
for(int i=0;i<n;++i){
if (s[i]=='L') ++l;
else ++r;
}
int ans=l+r+1;
cout<<ans<<endl;
}
B. Just Eat It! (CF 1285 B)
題目大意
給定一個數列,問整個區間和以及部分區間和哪個大。
解題思路
區間和最值的用\(dynamic\ programming\),特判最值是否在全部區間取的即可。\(dp[i]\)表示前\(i\)個數,其中第\(i\)個數必取的最大值。則\(dp[i]=\max(dp[i-1],a[i])\),答案為\(\max\limits_{1\leq i\leq n}(dp[i])\)。
對應為代碼注釋部分。
注意到區間可以理解為是去掉從開頭的連續幾個數以及從結尾的連續幾個數。去掉后如果和會變大或者不變,則說明存在某點的前綴和或后綴和小於等於\(0\),如此兩邊掃一遍也行。
神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N=1e5+8;
int n;
LL a[N];
bool check(){
LL sum=0;
for(int i=0;i<n;++i){
sum+=a[i];
if (sum<=0) return 0;
}
sum=0;
for(int i=n-1;i>=0;--i){
sum+=a[i];
if (sum<=0) return 0;
}
return 1;
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
//printf("Case #%d: ", i);
read(n);
for(int i=0;i<n;++i) read(a[i]);
if (check()) puts("YES");
else puts("NO");
}
return 0;
}
/* int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
//printf("Case #%d: ", i);
int n;
LL sum=0;
read(n);
LL a[n+5]={0};
for(int i=0;i<n;++i){
read(a[i]);
sum+=a[i];
}
LL ans=a[0];
LL dp[n+5]={0};
dp[0]=a[0];
bool qwq=true;
for(int i=1;i<n;++i){
if (dp[i-1]+a[i]>a[i]) dp[i]=dp[i-1]+a[i];
else {dp[i]=a[i]; qwq=false;}
ans=MAX(ans,dp[i]);
}
if (qwq) for(int i=0;i<n-1;++i) if (dp[i]==sum) qwq=false;
if (dp[n-1]==sum&&qwq&&ans==sum) ans=sum-1;
if (ans>=sum) printf("NO\n");
else printf("YES\n");
}
return 0;
} */
C. Fadi and LCM (CF 1285 C)
題目大意
給定\(x\),找出一對\(a,b\)使得\(lcm(a,b)=x\)且\(\max(a,b)\)最小。
解題思路
\(lcm(a,b)=\dfrac{a\times b}{gcd(a,b)}=x\),令\(a=k_1\times gcd(a,b),b=k_2\times gcd(a,b)\),則\(k_1\times k_2\times gcd(a,b)=x\),不失一般性,我們設\(a\leq b\)即\(k_1\leq k_2\),我們要最小化\(b\),即最小化\(k_2\times gcd(a,b)\),即最大化\(k_1\),即找到最大的\(k_1\)且\(k_1\leq k_2\),由於\(x\leq 10^{12}\),而\(k_1\leq \sqrt{x}\),所以我們從大到小枚舉\(k_1\)判斷\(lcm(a,b)\)是否等於\(x\)即可。我們也可以假設\(gcd(a,b)=1\),這是始終有解且一定是最小的。
神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
LL x=0;
read(x);
LL qwq=sqrt(x);
while(qwq){
if (x%qwq==0) if(__gcd(qwq,x/qwq)==1) break;
--qwq;
}
printf("%lld %lld\n",qwq,x/qwq);
return 0;
}
D. Dr. Evil Underscores (CF 1285 D)
題目大意
給定\(n\)個數\(a_i\ (i=1,2,3,...,n)\),要求找一個數\(X\),最小化\(\max\limits_{1\leq i\leq n}(a_i\oplus X)\)。輸出這個最小值。
解題思路
異或題盲猜trie
然后弄dp然后暴斃
經過分析我們發現,對於在二進制下的某一位,如果全都是\(1\)或者\(0\),我們都可以對\(X\)在這一位添\(1\)或\(0\)來變小,但如果某一位既有\(1\)又有\(0\),那這一位將不可避免的存在\(1\)。那么我們從高位起,判斷該位,如果全部是\(0\)或\(1\),那么答案在該位就是\(0\),否則將是\(1\),然后分\(X\)在該位是\(0\)或\(1\)兩種情況繼續搜下去即可。在\(trie\)樹上跑。
神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N=4e6+7;
int trie[N][2],n,m=1;
void add(int x){
int t=1;
for(int i=29;i>=0;--i){
if (!trie[t][(x>>i)&1]) trie[t][(x>>i)&1]=++m;
t=trie[t][(x>>i)&1];
}
}
int DFS(int t,int len){
if (len<0) return 0;
if (!trie[t][0]) return DFS(trie[t][1],len-1);
if (!trie[t][1]) return DFS(trie[t][0],len-1);
return (min(DFS(trie[t][0],len-1),DFS(trie[t][1],len-1))|(1<<len));
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read(n);
for(int u,i=1;i<=n;++i){
read(u);
add(u);
}
printf("%d\n",DFS(1,29));
return 0;
}
E. Delete a Segment (CF 1285 E)
題目大意
給定\(n\)條線段,線段之間若有交叉(端點值相同也算交叉)或者重合則可以聯合起來成為新的一條(可能)更長的線段,現在要求去掉一條線段(不能不去),使得去掉后聯合后的線段最多,問是多少。
解題思路
我們考慮去掉一條線段后能夠增加多少個線段。
我們可以發現,如果該線段所覆蓋的區間里,存在某個子區間,它只被該線段覆蓋的話,去掉該線段后,這里就有一個空隙。而如果有\(x\)個空隙出來的話,那就會多出\(x\)個線段出來。這啟示我們從空隙數量的角度來解決聯合線段的個數。
我們考慮如何求只被該線段覆蓋的區間的數量。
我們按左端點從小到大排列枚舉,對於當前枚舉的這條線段\(i\),前\(i-1\)條線段的右端點的最大值為\(r\),次大值為\(r_0\),則對於其右端點為\(r\)的某條線段\(j\)來說,它所覆蓋的\([l_j,r_j]\)區間中,\((r_0,r_j]\)區間(或\([l_j,r_j]\),如果\(l_j>r_0\))並沒有被其他線段覆蓋,如果第\(i\)條線段的左端點\(l_i>r_0\)的話,那么區間\((r_0,l_i)\)這部分區間就只被線段\(j\)覆蓋,然后我們更新\(r\)及\(r_0\)重復操作,得到每條線段覆蓋的區間內沒被其他線段覆蓋的區間數\(cnt_i\)(或者說線段內的空隙),還有本身存在的空隙\(gap\),最后由植樹原理得答案\(ans=gap+\max\limits_{1\leq i\leq n}(cnt_i)+1\)。
要特判全部線段沒有交叉的情況,此時最終答案應是\(ans=n-1\)。
神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
//printf("Case #%d: ", i);
int n;
vector<pair<int,int>> a;
read(n);
for(int l,r,i=0;i<n;++i){
read(l);
read(r);
a.push_back(make_pair(l,r));
}
sort(a.begin(),a.end(),less<pair<int,int>>());
int r=a[0].second,ans=0,ma=-2147483644,cnt=0,gap=0;
for(int i=1;i<n;++i){
if (a[i].first>r){
++gap;
r=a[i].second;
ma=-2147483644;
ans=max(ans,cnt);
cnt=0;
}else if (ma==-2147483644){
ma=min(r,a[i].second);
r=max(r,a[i].second);
}else{
if (a[i].first>ma) ++cnt;
if (a[i].second>=r){
ans=max(ans,cnt);
cnt=0;
}
ma=max(ma,min(r,a[i].second));
r=max(r,a[i].second);
}
}
ans=max(ans,cnt);
ans=ans+gap+1;
if (gap+1==n) ans=n-1;
printf("%d\n",ans);
}
return 0;
}
F. Classical? (CF 1285 F)
題目大意
給定\(n\)個數\(a_i\),求\(\max\limits_{1 \le i < j \le n} lcm(a_i,a_j)\)。
解題思路
\(lcm(a_i,a_j)=\dfrac{a_i\times a_j}{gcd(a_i,a_j)}\)。我們可以枚舉\(gcd(a_i,a_j)\),然后所有數都除以\(gcd(a_i,a_j)\),這樣我們要尋找的就是兩個互質的數\(a_x,a_y\),它們的乘積最大。我們從大到小遍歷這些數,對於當前的數\(a_i\),我們想知道大於它的數中是否有與它互質的,即求\(\sum\limits_{a_j>a_i}[gcd(a_i,a_j)=1]\)是否大於零。這是個經典式子,由於\([gcd(a_i,a_j)=1]=\sum_\limits{d|gcd(a_i,a_j)}\mu(d)\),我們對其變形:
我們改變它的求和順序,由於\(d\)肯定是\(a_i\)的因子,我們枚舉它的每一個因子,這樣的因子在求和式中出現次數為\(cnt_d\),得
其中\(cnt_d\)表示大於\(a_i\)的數中是\(d\)的倍數的個數,\(\mu(x)\)是莫比烏斯函數。
這樣我們維護一個\(cnt\)數組,就能夠知道是否有與\(a_i\)互質的數,拿一個指針往回掃,掃到那個互質的數,然后計算答案\(a_i*a_j*gcd(a_i,a_j)\)(因為這里的兩個數\(a_i,a_j\)都除以了一次\(gcd(a_i,a_j)\))。注意下次再遇到有互質的時候,指針從上次的位置繼續往回找即可,不需要重新回到原來位置往回掃,因為我們是從大到小遍歷這些數,小的數與前面的數的乘積不可能大於原先的答案,這里可以用棧處理。
時間復雜度為\(O(\sum\limits_{i=1}^{n}\sigma_0(i)^2)\),其中\(\sigma_0(n)=\sum\limits_{d|n}1\)。
還可以再優化一下,我們把\(\dfrac{a_i\times a_j}{gcd(a_i,a_j)}\)視為\(a_i\times \dfrac{a_j}{gcd(a_i,a_j)}\),於是把\(a_j\)的每個因子都儲存下來,這樣雖然會增加原本不應有的答案,但這些答案會小於\(a_j\)對應的答案,對我們要找的最大值無影響。
也就是說我們對於每個\(a_i\),把它的因子\(d_{ij}\)全部加到數組里,因為拿\(a_i\)與另一個數求\(LCM\)不會小於拿它的某個因子\(d_i\)與另一個數求\(LCM\),所以這對最終答案沒有影響,盡管會增加原本不會出現的答案。然后再從大到小枚舉\(a_i\),尋找比\(a_i\)大的且與\(a_i\)互質的數,這仍是上面的方法,只是省去了枚舉\(gcd(a_i,a_j)\)的操作,最終的時間復雜度為\((\sum\limits_{i=1}^{n}\sigma_0(i))\)
神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N=1e5+8;
bool sign[N];
int n,ma,u[N],cnt[N];
LL ans;
vector<int> d[N];
stack<LL> s;
void pre(){
for(int i=1;i<=ma;++i){
for(int j=i;j<=ma;j+=i) d[j].push_back(i);
if (i==1) u[i]=1;
else if (i/d[i][1]%d[i][1]==0) u[i]=0;
else u[i]=-u[i/d[i][1]];
}
}
int coprime(int x){
int tmp=0;
for(int i:d[x])
tmp+=u[i]*cnt[i];
return tmp;
}
void updata(int x,int val){
for(int i:d[x])
cnt[i]+=val;
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read(n);
for(int a,i=1;i<=n;++i){
read(a);
sign[a]=true;
ma=MAX(ma,a);
}
pre();
for(int i=1;i<=ma;++i)
for(int j=2;i*j<=ma;++j)
sign[i]|=sign[i*j];
for(int qwq,i=ma;i>=1;--i){
if (!sign[i]) continue;
qwq=coprime(i);
while(qwq){
if (__gcd(s.top(),(LL)i)==1){
ans=max(s.top()*(LL)i,ans);
--qwq;
}
updata(s.top(),-1);
s.pop();
}
updata(i,1);
s.push((LL)i);
}
write(ans,'\n');
return 0;
}