Codeforces Round #562 (Div. 1)
A:
題意是給定一個數組,每次可以對數組中的任意一個子集中的元素自增1,求最少需要多少次操作使得這個數組非降序。
顯然可以看出操作次數對這個問題滿足二分性質的,二分一下就行了。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
int a[N];
int n, m;
bool check(int k) {
int p;
if(a[1]+k>=m) p=0;
else p=a[1];
for(int i=2; i<=n; ++i) {
// cout << p << endl;
if(a[i]+k<p) return false;
if(a[i]+k>=m&&(a[i]+k)%m>=p) continue;
if(a[i]<=p) continue;
else p=a[i];
}
return true;
}
int main() {
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
int l=0,r=m-1;
// check(0);
// exit(0);
while(l<r) {
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n", r);
}
B:
題意是 的這樣的三元組,如果三個數都相等,那麼就是合法的三元組。
求有多少對 使得在 上有這樣的合法三元組。
可以用bitset對 小於 的情況算出來,進行剪枝,剩下的部分暴力計算。隨機情況下剩下的部分是個位數,因此複雜度大概就是bitset這部分。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
const int B = 1e4;
bitset<N> b;
bitset<N> p;
char s[N];
int f[N], n;
//int c = 0;
void cal() {
for(int i=0; i<n; ++i) p.set(i);
for(int i=1; i<=min(n/2, B); ++i) {
bitset<N> t = p&b&(b>>i)&(b>>(i*2));
// if(i==2) cout << t._Find_first() << " " << t.test(0) << endl;
for(int j=t._Find_first(); j<n; j=t._Find_next(j)) {
// printf("f[%d]=%d\n",j, i);
// ++c;
f[j]=i;
}
p &= t.flip();
}
bitset<N> t = p&b;
vector<int> a;
for(int j=t._Find_first(); j<n; j=t._Find_next(j)) {
a.push_back(j);
// printf("%d\n", j);
}
if(n/2<=B||a.empty()) return;
for(int i=0; i<a.size(); ++i) {
int v = a[i];
for(int j=B+1; v+2*j<n; ++j) {
int l=v+j, r=v+2*j;
if(b.test(l)&&b.test(r)) {
f[a[i]]=j;
// printf("f[%d]=%d\n",j, i);
break;
}
}
}
}
int main() {
scanf("%s", s);
n =strlen(s);
for(int i=0; i<n; ++i) {
if(s[i]=='1') b.set(i);
f[i]=n;
}
cal();
for(int i=0; i<n; ++i) {
b.flip(i);
}
cal();
int mn = n;
long long ans=0;
// for(int i=0;i<n;++i) printf("%d\n", f[i]);
for(int i=n-1; i>=0; --i) {
mn=min(mn, i+f[i]*2);
ans += n-mn;
}
// printf("%d\n", c);
printf("%I64d\n", ans);
}
C:
題意是對於每個詢問 ,求出是否存在一個子序列,這個子序列提取出來後,第一個數是 , 最後一個數是,且子序列相鄰兩個數按位與都大於0 ,這種情況稱爲 到 可達。
設 爲第一個 且 中位 爲 的數的下標。這樣對於一個詢問 就可以枚舉 被置1的所有位 ,求 的最小值,看是否小於等於 即可。
的計算方法是對於所有 被置1的位,枚舉同樣被置1的右邊第一個數 , 的最小值等於 了。
本題的關鍵是能想出這樣表示 的話可以分別表示到 可達和到 可達。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
const int B = 19;
int last[B];
int dp[N][B];
// dp[i][j] 表示大於等於i的第一個可達i且位j被置位的點的下標。
int a[N];
int main() {
int n, q;
scanf("%d%d", &n, &q);
for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
for(int i=0; i<B; ++i) last[i] = N;
for(int i=n; i>=1; --i) {
int k = a[i];
for(int j=0; j<B; ++j) {
if((k>>j)&1) {
dp[i][j] = i;
} else {
dp[i][j] = N;
for(int p=0; p<B; ++p) {
if((k>>p)&1&&last[p]!=N) {
dp[i][j] = min(dp[i][j], dp[last[p]][j]);
}
}
}
}
for(int j=0; j<B; ++j) {
if((k>>j)&1) last[j]=i;
}
}
while(q--) {
int x, y;
scanf("%d%d", &x, &y);
int ans = N;
for(int i=0; i<B; ++i) {
if((a[y]>>i)&1) {
ans = min(ans, dp[x][i]);
}
}
if(ans<=y) puts("Shi");
else puts("Fou");
}
return 0;
}