目录
A-Bridging the Gap 2
考虑船上至少L个人,每次相当于向左转移(R-L)个人,一共需要(n-l)/(r-l)次向左,需要向右(n-l)/(r-l)-1次,一共需要l*((n-l)/(r-l)-1)次,计算出每个人能贡献的向左的次数,累加看看是否大于等于需要的总次数,但可能某些人体力过大,记得取min
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 1e6+10;
int a[N];
void solve(){
int sum=0;
int n,l,r;
cin>>n>>l>>r;
int res;
if((n-l)%(r-l)==0){
res=(n-l)/(r-l);
}else{
res=(n-l)/(r-l)+1;
}
// cout<<res<<'\n';
// res=(res-1)*l;
int ans=(res-1)*l;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
a[i]=(a[i]-1)/2;
sum+=min(a[i],res-1);
}
if(sum>=ans)cout<<"Yes"<<'\n';
else cout<<"No"<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}
B-Crash Test
取个gcd即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 1e6+10;
void solve(){
int n,m;
cin>>n>>m;
int g=-1;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(g==-1)g=x;
else g=__gcd(g,x);
}
int ans=m%g;
ans=min(ans,g-ans);
cout<<ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}
D-Dominoes!
如果一个数字出现了超过 n + 2次,一定无解
容易看出来如果左右都不同很容易构造(左右交换即可)
将所有 xi = yi的骨牌按照每个点数的出现次数放在一个堆中,每次选择 出现次数最大(且和上一个放置的不同)的骨牌放在右侧。 如果发现剩下的 xi = yi的骨牌点数都和上一个相同,不妨设该点数为 p, 从 xi ̸= yi的骨牌堆中挑一个 xi , yi ̸= p的骨牌作为间隔。
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 2e5 + 10;
int n, sn = 0;
stack<pair<int, int>> b1;
map<int, int> m,mp;
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
int u, v;
cin >> u >> v;
if (u == v) {
m[u]++;
} else {
b1.emplace(u, v);
}
mp[u]++;
mp[v]++;
if(mp[u]>n+1||mp[v]>n+1){
cout<<"No"<<'\n';
return;
}
}
cout<<"Yes"<<'\n';
priority_queue<pair<int, int>> q;
for (auto [x, y]: m) {
q.push({y, x});
}
while (q.size() >= 2) {
auto c1 = q.top();
q.pop();
auto c2 = q.top();
q.pop();
b1.push({c1.second, c2.second});
sn++;
if (c1.first > 1)q.push({c1.first - 1, c1.second});
if (c2.first > 1)q.push({c2.first - 1, c2.second});
}
pair<int, int> sa = {0, 0};
if (!q.empty())sa = q.top();
int now = -1;
while (!b1.empty() || (now != sa.second && sa.first)) {
if (now != sa.second && sa.first) {
cout<<sa.second<<" "<<sa.second<<'\n';
sa.first--;
now = sa.second;
}else{
pair<int, int> cur = b1.top();
b1.pop();
int x = cur.first, y = cur.second;
if (x == now)swap(x, y);
if (sn) {
sn--;
cout<<x<<" "<<x<<'\n';
cout<<y<<" "<<y<<'\n';
} else {
cout<<x<<" "<<y<<'\n';
}
now = y;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin>>T;
while (T--)solve();
return 0;
}
E-Malfunctioning Typewriter
跑一个字典树,n个串都得出现一次,相当于每个结点都会跑过对应的次数,每个节点对答案的贡献是独立的,相当于是它的左右儿子的01的个数对应的概率,这个概率可以预处理出来。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 100;
const int mod = 1000000007;
const int INF = 0x3f3f3f3f;
int n,m;
double p ;
double f[2020][2020];
int tire[N][3];
int tot =0 ;
int siz[N];
void insert(string s){
int id = 0 ;
for(int i=0;i<s.size();i++){
int val =s[i]-'0';
if(tire[id][val] ==0 ){
tire[id][val] = ++tot;
}
id = tire[id][val];
siz[id]++;
}
}
double ans ;
void dfs(int p){
int siz1 = siz[tire[p][1]],siz0=siz[tire[p][0]];
ans *= (f[siz1][siz0]);
if(siz1)dfs(tire[p][1]);
if(siz0)dfs(tire[p][0]);
}
void solve() {
cin>>n>>m>>p;
f[0][0] = 1;
for(int i=1;i<=n;i++){
f[i][0] = f[i-1][0] * p;
f[0][i] = f[0][i-1]* p;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j] = max((p)*f[i-1][j] + (1-p)*f[i][j-1] ,(1-p)*f[i-1][j] + (p)*f[i][j-1] );
}
}
ans = 1;
for(int i=1;i<=n;i++){
string s;
cin>>s;
insert(s);
}
dfs(0);
cout<<fixed<<setprecision(12)<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}
H-Rectangle Intersection
对于每一个方格,它所在的最小的交集是它上下左右离它最近的矩形包围而成的,但暴力时间不够。
可以发现一整块的这种贡献,是可以用它所在的交集的一个角来代替的,可以用前缀和的思想,找到这些边界点求答案。
0123分别表示上下左右
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e3+50;
int s[4][N][N];
int n,m,k,lx,ly,rx,ry;
int ans=1e15;
void solve(){
cin>>n>>m>>k;
for(int i=0;i<=k;i++){
if(i==0) lx=1,ly=1,rx=n,ry=m;
else cin>>lx>>ly>>rx>>ry;
s[0][lx][ry]++,s[0][rx+1][ry]--;
s[1][lx][ly]++,s[1][rx+1][ly]--;
s[2][lx][ly]++,s[2][lx][ry+1]--;
s[3][rx][ly]++,s[3][rx][ry+1]--;
}
for(int i=0;i<=1;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
s[i][j][k]+=s[i][j-1][k];
for(int i=2;i<=3;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
s[i][j][k]+=s[i][j][k-1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[1][i][j]&&s[2][i][j]){
int x=i,y=j;
int len1=1,len2=1;
while(!s[0][i][y]) y++,len1++;
while(!s[3][x][j]) x++,len2++;
ans=min(ans,len1*len2);
}
}
}
cout<<ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
J-Rigged Games
先处理出从每一个点开始,下一次赢下一个小局是在哪个点,(可以双指针O(n),赛时用二分多了个log)然后用倍增+二分求出答案,赛时这里二分的是跳几次,其实去算什么时候达到B的话也可以不用二分(类似于LCA时先跨大步后跨小步的做法)
多了两个二分的代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 1e6+10;
string s;
int sum1[N],sum0[N];
int a[N][25],b[N][25];
int n,aa,bb;
int check(int k,int x){
int res=0;
for(int i=0;i<=20;i++){
if((x>>i)&1){
res+=b[k][i];
k=a[k][i];
}
}
if(res>=bb)return 1;
else if((x-res>=bb))return 0;
else return -1;
}
void solve(){
cin>>n>>aa>>bb;
cin>>s;
string tt=s;
while(s.size()< n+3*aa+1){
s=s+s;
}
sum1[0]=sum0[0]=0;
for(int i=0;i<s.size();i++){
sum1[i+1] =sum1[i];
sum0[i+1]=sum0[i];
if(s[i]=='0')sum0[i+1]++;
else sum1[i+1]++;
// if(i<=20)cout<<i<<' '<<sum0[i]<<'\n';
}
for(int i= 1;i<= n;i++){
// cout<<"sum0"<<sum0[i]<<endl;
int l = i ,r = (int)s.size();
while(l<=r){
int mid =(l+r)/2;
int y1 = sum1[mid] -sum1[i-1];
int y0 = sum0[mid] -sum0[i-1];
if(y1>=aa ||y0>=aa)r=mid-1;
else l =mid+1;
}
a[i][0] = (l)%n+1 ;
int y1 = sum1[l] - sum1[i-1];
// int y0 = sum0[l] -sum0[i-1];
// cout<<i<<" "<<l<<':'<<y1<<' '<<sum0[l]<<' '<<sum0[i-1]<<'\n';
if(y1>=aa)b[i][0]=1;
}
for(int i=1; i<=20; i++){
for (int j = 1; j <= n; j++) {
a[j][i]=a[a[j][i-1]][i-1];
b[j][i]=b[j][i-1]+b[a[j][i-1]][i-1];
}
}
for(int i=1;i<=n;i++){
int l=bb,r=2*bb,mid;
while(l<=r){
mid=(l+r)/2;
if(check(i,mid)!=-1)r=mid-1;
else l=mid+1;
}
cout<<check(i,l);
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}
L-Sudoku and Minesweeper
找个不在边界的8就好了
#include<bits/stdc++.h>
#define int long long
using namespace std;
int mp[100][100];
string s;
int x,y;
void solve(){
for(int i=1;i<=9;i++){
cin>>s;
for(int j=1;j<=9;j++){
mp[i][j]=s[j-1]-'0';
}
}
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
if(mp[i][j]==8&&i<=6&&i>=4&&j<=6&&j>=4){
x=i,y=j;
break;
}
}
}
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
if(i==x&&j==y) cout<<8;
else cout<<'*';
}
cout<<'\n';
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int _ = 1;
// cin >> _;
while (_--) solve();
return 0;
}