牛客小白月賽39
阿新 • • 發佈:2021-10-23
solve: 5/8
A題:
$O(N^2)$暴力一下搜一下兩個向量的組合
#include<bits/stdc++.h> using namespace std; #define ll long long inline ll read() { ll x = 0, f = 1; char ch = getchar(); for(; ch < '0' || ch>'9'; ch = getchar()) if(ch == '-') f = -f; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } inline void chkmin( int &a, int b ) { if(a > b) a = b; } inline void chkmax( int &a, int b ) { if(a < b) a = b; } #define _ read() #define ln endl const int N=1005; int n, x[N], y[N]; int main(){ n=read(); for(int i=1; i<=n; i++) x[i]=read(), y[i]=read(), x[i]=read()-x[i], y[i]=read()-y[i]; int X=read(), Y=read(); X=read()-X; Y=read()-Y; for(int i=1; i<=n; i++) for(int j=i+1; j<=n; j++) if((x[i]+x[j])*Y-(y[i]+y[j])*X==0) return puts("YES"),0; puts("NO"); }
B題:
暴力找"QAQ"即可(反正是子串)
#include<bits/stdc++.h> using namespace std; #define ll long long inline ll read() { ll x = 0, f = 1; char ch = getchar(); for(; ch < '0' || ch>'9'; ch = getchar()) if(ch == '-') f = -f; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } inline void chkmin( int &a, int b ) { if(a > b) a = b; } inline void chkmax( int &a, int b ) { if(a < b) a = b; } #define _ read() #define ln endl char s[1000006]; int n; int main(){ scanf("%s", s+1); n=strlen(s+1); for(int i=3; i<=n; i++) if(s[i-2]=='Q'&&s[i-1]=='A'&&s[i]=='Q') return cout<<i-2<<ln,0; }
D題:
可以發現如果ai是質數, 那麼ai乘上一個不等於1的數肯定是合數, 根據這個我們只需要一個區間set
可以修改可以不管a1那個數, 但是統計要統計上
但是需要考慮ai=1的情況, 此時如果他出現在2, 3, 5, 7……這些質數位置上, 那麼有可能變成質數, 於是我們需要一個單點加
我的做法是: 搞一個set存所有的可能變成質數的地方, 然後每次查詢將set中在L,R中的點提出來, 然後在set中刪掉該點, 如果x=1那麼給該點暴力+1,
其他的地方按一開始ai是否為質數賦值為0,1, 然後建一棵線段樹暴力維護即可
效率$O(Nlog_2N)$
#include<bits/stdc++.h> using namespace std; #define ll long long inline ll read() { ll x = 0, f = 1; char ch = getchar(); for(; ch < '0' || ch>'9'; ch = getchar()) if(ch == '-') f = -f; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } inline void chkmin( int &a, int b ) { if(a > b) a = b; } inline void chkmax( int &a, int b ) { if(a < b) a = b; } #define _ read() #define ln endl const int N=5e5+5; int n, m, vis[N], a[N]; int t[N<<2], lzy[N<<2]; set<int> s; void up(int x){ t[x]=t[x<<1]+t[x<<1|1]; } void build(int l, int r, int rt){ if(l==r){t[rt]=a[l]; return;} int mid=(l+r)>>1; build(l, mid, rt<<1); build(mid+1, r, rt<<1|1); up(rt); } void down(int x){ if(lzy[x]){ t[x<<1]=0; t[x<<1|1]=0; lzy[x<<1]|=lzy[x]; lzy[x<<1|1]|=lzy[x]; lzy[x]=0; } } int query(int L, int R, int l, int r, int rt){ if(L<=l&&r<=R) return t[rt]; int mid=(l+r)>>1; down(rt); int ans=0; if(L<=mid) ans+=query(L, R, l, mid, rt<<1); if(R>mid) ans+=query(L, R, mid+1, r, rt<<1|1); return ans; } void add(int x, int l, int r, int rt){ if(l==r){t[rt]=1; return;} int mid=(l+r)>>1; down(rt); if(x<=mid) add(x, l, mid, rt<<1); else add(x, mid+1, r, rt<<1|1); up(rt); } void modify(int L, int R, int l, int r, int rt){ if(L<=l&&r<=R){t[rt]=0; lzy[rt]=1; return;} int mid=(l+r)>>1; down(rt); if(L<=mid) modify(L, R, l, mid, rt<<1); if(R>mid) modify(L, R, mid+1, r, rt<<1|1); up(rt); } int main(){ n=read(); m=read(); for(int i=2; i<=500000; i++) vis[i]=1; for(int i=2; i<=500000; i++) if(vis[i]){ for(int j=2; i*j<=500000; j++) vis[i*j]=0; } for(int i=1; i<=n; i++) a[i]=read(); for(int i=2; i<=n; i++) if(vis[i]&&a[i]==1) s.insert(i); for(int i=1; i<=n; i++) a[i]=vis[a[i]]; build(1, n, 1); while(m--){ int opt=read(), l=read(), r=read(); if(opt==2) printf("%d\n", query(l, r, 1, n, 1)); else { int x=read(); int L=l, R=r; if(L==1) ++L;//L=1修改之後仍然等於本身 if(L<=R&&x!=0) modify(L, R, 1, n, 1);//一旦修改肯定不是質數 set<int>::iterator it=s.lower_bound(L);//L..R中間的1 set<int>::iterator it2=it; for(;it!=s.end()&&*it<=R; ++it) if(x==1) add(*it, 1, n, 1); if(it2!=s.end()&&x!=0) s.erase(it2, it);//為0不用刪 printf("%d\n", query(l, r, 1, n, 1)); } } }
E題:
直接分解然後從後往前乘起來即可, 詳細看程式碼
#include<bits/stdc++.h> using namespace std; #define ll long long inline ll read() { ll x = 0, f = 1; char ch = getchar(); for(; ch < '0' || ch>'9'; ch = getchar()) if(ch == '-') f = -f; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } inline void chkmin( int &a, int b ) { if(a > b) a = b; } inline void chkmax( int &a, int b ) { if(a < b) a = b; } #define _ read() #define ln endl int main(){ int n=read(); ll ans = 0; for(int i=1; i<=n; i++){ int x=read(); int tmp=0; while(x){tmp=tmp*2+x%2; x>>=1;} ans=ans+tmp; } cout<<ans<<ln; }
G題:
根據每個數都可以拆分成若干個質數乘積的唯一分解, 我們可以用每一個數去篩他的倍數, 當然如果用上一點埃篩的思想可以優化到$O(NlnlnN)$, 但是可以暴力$O(NlnN)$幹他
然後我們只需要對每個質數維護他的字尾和即可, 可以用樹狀陣列方便的實現
效率O(Nlog_2N)
#include<bits/stdc++.h> using namespace std; #define ll long long inline ll read() { ll x = 0, f = 1; char ch = getchar(); for(; ch < '0' || ch>'9'; ch = getchar()) if(ch == '-') f = -f; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } inline void chkmin( int &a, int b ) { if(a > b) a = b; } inline void chkmax( int &a, int b ) { if(a < b) a = b; } #define _ read() #define ln endl const int N=3000005; int f[N], ans[N]; struct node{int n, k, id;}q[N]; bool cmp(node a, node b){return a.n<b.n;} int t[N]; inline int lowbit(int x){return x&-x;} inline void add(int x){for(;x; x-=lowbit(x)) t[x]++;} inline int ask(int x){int res=0; for(;x<=3000000; x+=lowbit(x)) res+=t[x]; return res;} int main(){ for(int i=1; i<=3000000; i++) f[i]=i; for(int i=2; i<=3000000; i++) for(int j=1; i*j<=3000000; j++) f[i*j]=min(i, f[i*j]);//質數唯一表示定理, 最小的質數因子 // cout<<f[100]<<ln; int m=read(); for(int i=1; i<=m; i++) q[i].n=read(), q[i].k=read(), q[i].id=i; sort(q+1, q+m+1, cmp); for(int i=1; i<=m; i++){ for(int j=q[i-1].n+1; j<=q[i].n; j++) if(j!=1) add(f[j]); ans[q[i].id]=ask(q[i].k); } for(int i=1; i<=m; i++) printf("%d\n", ans[i]); }
下面是補題的:
finish: 1/3
H題:
假設沒有膜法的存在, 我們只需要從左往右貪心地砍小怪, 如果ai沒被ai-1和ai-2砍光, 那麼必須要砍, 每次取最小的代價
但是由於有膜法的存在, 我們需要統計從左往右砍的代價L[i], 和從右往左砍的代價R[i]
那麼答案就是min{L[i]+R[i+3]}, 此時i+1和i+2不需要砍到
#include<bits/stdc++.h> using namespace std; #define ll long long inline ll read() { ll x = 0, f = 1; char ch = getchar(); for(; ch < '0' || ch>'9'; ch = getchar()) if(ch == '-') f = -f; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } inline void chkmin( int &a, int b ) { if(a > b) a = b; } inline void chkmax( int &a, int b ) { if(a < b) a = b; } #define _ read() #define ln endl const int N=1e6+5; int n, a[N]; ll b[N], L[N], R[N], ans; int main(){ n=read(); for(int i=1; i<=n; i++) a[i]=read();//f[i][0/1]表示到i為止, 有沒有用過魔法 // L[0]=R[n+1]=0; ans=2e18; L[1]=b[1]=a[1]; for(int i=2; i<=n; i++)//逐個砍 L[i]=L[i-1]+max(a[i]-b[i-1]-b[i-2], 0ll), b[i]=max(a[i]-b[i-1]-b[i-2], 0ll); memset(b, 0, sizeof(b)); R[n]=b[n]=a[n]; for(int i=n; i; i--) R[i]=R[i+1]+max(a[i]-b[i+1]-b[i+2], 0ll), b[i]=max(a[i]-b[i+1]-b[i+2], 0ll); for(int i=0; i<=n; i++) ans=min(ans, L[i]+R[i+3]); // cout<<R[3]<<ln; cout<<ans<<ln; } /* 記錄對上一個位置和對前一個位置的斬擊 能確定對當前位置需要使用多少斬擊 max(a[i]-b[i-1]-b[i-2], 0) */