A. 嚎叫響徹在貪婪的廠房
分析
玄學題目,玄學題面,考場沒寫判重直接抱零。
因為要求一段連續的數是一個等差數列的子序列,那么容易想到這段序列里每兩個數的差的絕對值一定都不是互質的,所以 \(O(n)\) 掃一遍就好了。當遇到重復的數或者差互質的時候,就清空用來判重的東西,然后 \(ans++\) 就行了。判重的話 \(set\) 和 \(map\) 隨意。
代碼
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long
const int maxn = 1e5+10;
int a[maxn];
map<int,int>mp;
inline int gcd(int x,int y){
if(y == 0)return x;
return gcd(y,x%y);
}
int main(){
int ans = 1;
int n;
scanf("%d",&n);
for(int i = 1;i <= n;++i){
scanf("%d",&a[i]);
}
int d2 = abs(a[2] - a[1]);
mp[a[1]] = 1;
for(int i = 2;i <= n;++i){
int d = abs(a[i] - a[i-1]);
if(mp[a[i]]){//判重
mp.clear();//清空
ans++;
d2 = abs(a[i] - a[i+1]);//更新差值
mp[a[i]] = 1;
continue;
}
d2 = gcd(d,d2);
mp[a[i]] = 1;
if(d2 <= 1){//互質
ans++;
mp.clear();
mp[a[i]] = 1;
d2 = abs(a[i] - a[i+1]);
}
}
printf("%d\n",ans);
}
B. 征途堆積出友情的永恆
分析
考場把暴力分拿滿了。 \(50\) 分寫個 \(dp\) 就行了,剩下的 \(30\) 特判一下,瞎推一推就行。
其實 \(50\) 分的 \(dp\) 優化一下就是正解,考場沒想,也應該想不出來。考慮朴素 \(dp\) ,\(f[i] = min(f[i],f[j] + max(sum[i]-sum[j],b[j]))\) 。考慮怎么表示后邊的那個東西,后邊那個可拆成兩部分, \(f[j]+sum[i]-sum[j]\) 和 \(f[j]+b[j]\) ,然后我們要求的是最小值,而 \(j\) 是不定的, \(i\) 是確定的,所以我們可以只考慮 \(f[j]-sum[j]\) 和 \(f[j]+b[j]\) ,然后我們就可以進行優化。因為越小越好,所以我們開兩個小根堆,分別存那兩個值,設當前值的位置為 \(id\) ,那么當 \(id < i-k\) 時,直接 \(pop\) 掉,如果 \(f[j]+b[j]\) 這個堆的堆頂小於 \(f[j]-sum[j]\) 的堆頂,那么由於我們取的是最大值,前者肯定用不到了,所以我們就可以把它刪掉,把那個位置的 \(f[id]-sum[id]\) 放到第二個堆里就行了。最后取兩個堆頂的最小值更新答案。
代碼
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct Node{
ll id,val;
Node(){}
Node(int x,int y){
id = x,val = y;
}
friend bool operator < (const Node& a,const Node& b){
return a.val > b.val;
}
};
priority_queue<Node>q1,q2;
const int maxn = 5e5+10;
int a[maxn],b[maxn];
ll sum[maxn],f[maxn];
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++i){
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
}
for(int i = 1;i <= n;++i){
scanf("%d",&b[i-1]);
}
memset(f,0x3f,sizeof(f));
f[0] = 0;
for(int i = 1;i <= n;++i){
q1.push(Node(i-1,f[i-1] + b[i-1]));
while(!q1.empty()){//判斷是否能直接坐到這里
Node s = q1.top();
if(s.id < i - k)q1.pop();
else break;
}
while(!q2.empty()){
Node s = q2.top();
if(s.id < i - k)q2.pop();
else break;
}
while(!q1.empty()){//處理f[j]+b[j] 這個堆的堆頂小於 f[j]-sum[j] 的堆頂的情況
Node s = q1.top();
int j = s.id;
int val = s.val;
if(val > f[j] + sum[i] - sum[j])break;
q1.pop();
q2.push(Node(j,f[j]-sum[j]));
}
while(!q1.empty()){//同第1,2個while循環
Node s = q1.top();
if(s.id < i - k)q1.pop();
else break;
}
while(!q2.empty()){
Node s = q2.top();
if(s.id < i - k)q2.pop();
else break;
}
ll jl = 0x3f3f3f3f3f3f3f3f;
if(!q1.empty())jl = min(jl,q1.top().val);
if(!q2.empty())jl = min(jl,q2.top().val + sum[i]);
f[i] = jl;
}
printf("%lld\n",f[n]);
return 0;
}
80分代碼
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn = 5e5+10;
int a[maxn],b[maxn];
ll f[maxn];
ll sum[maxn];
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++i){
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
}
int mx = 0;
for(int i = 1;i <= n;++i){
scanf("%d",&b[i-1]);
mx = max(mx,b[i-1]);
}
if(mx == 1){
printf("%lld\n",sum[n]);
return 0;
}
bool flag = 0;
for(int i = 1;i <= n;++i){
if(a[i] != b[i-1])flag = 1;
}
if(!flag){
printf("%lld\n",sum[n]);
return 0;
}
if(n == k){
printf("%lld\n",max((ll)b[0],sum[n]));
return 0;
}
if(n <= 10000 && k <= 1000){
memset(f,0x3f,sizeof(f));
f[0] = 0;
for(int i = 1;i <= n;++i){
for(int j = 1;j <= min(i,k);++j){
int cost = sum[i] - sum[i-j];
if(cost <= b[i-j])cost = b[i-j];
f[i] = min(f[i],f[i-j] + cost);
}
}
printf("%lld\n",f[n]);
return 0;
}
return 0;
}
C. 小奇的倉庫
分析
單純求距離和的話直接換根 \(dp\) 就好了,關鍵在於異或 \(m\) ,發現 \(m\) 最大是 \(15\) ,那么也就是說只會對后四位進行更改,所以從這里入手。首先求出所有點到除它以外的所有點的距離(也就是 \(m\) 為 \(0\) 的情況)。然后先找一個根,記錄一下樹上每個節點上,子樹中到它的距離后四位分別是 \(1\to 15\) 的點的個數,然后利用換根 \(dp\) 的思想,把這個只計算子樹的個數拓展到當前節點之外所有點到當前點距離后四位是 \(1\to 15\) 的個數,然后最后計算答案的時候,只需要用當前這個節點不異或的答案,加上 \(1\to 15\) 每種情況異或后的值減去異或前的值再乘以到這個點后四位為異或前的值的個數即可。
30分換根dp
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int L = 1 << 20;
char buffer[L],*S,*T;
#define gc (S == T && (T = (S = buffer) + fread(buffer,1,L,stdin),S == T) ? EOF : *S++)
inline int read(){
int s = 0 ,f = 1;char ch = gc;
for(;!isdigit(ch);ch = gc)if(ch == '-')f = -1;
for(;isdigit(ch);ch = gc)s = s * 10 + ch - '0';
return s * f;
}
const int maxn = 1e5+10;
struct Node{
int v,next,val;
}e[maxn<<1];
int head[maxn],tot;
int dis[maxn],vis[maxn];
int n,m;
namespace Mis0{
const int maxn = 1e5+10;
int ans[maxn],dis[maxn],siz[maxn];
inline void Add(int x,int y,int z){
e[++tot].v = y;
e[tot].next = head[x];
e[tot].val = z;
head[x] = tot;
}
inline void dfs1(int x,int f){
siz[x] = 1;
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(v == f)continue;
dis[v] = dis[x] + e[i].val;
dfs1(v,x);
siz[x] += siz[v];
}
}
inline void dfs2(int x,int f){
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(v == f)continue;
ans[v] = ans[x] - siz[v] * e[i].val + (n - siz[v]) * e[i].val;
dfs2(v,x);
}
}
int main(){
for(int i = 1;i < n;++i){
int x = read(),y = read(),z = read() ^ m;
Add(x,y,z);
Add(y,x,z);
}
dfs1(1,0);
for(int i = 1;i <= n;++i){
ans[1] += dis[i];
}
dfs2(1,0);
for(int i = 1;i <= n;++i){
printf("%d\n",ans[i]);
}
return 0;
}
}
int main(){
n = read(),m = read();
Mis0::main();
return 0;
}
滿分代碼
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5+10;
struct Node{
int v,next,val;
}e[maxn<<1];
int head[maxn],tot,f[maxn],g[maxn][20],b[maxn][20];
int dis[maxn];
int siz[maxn];
int n,m;
inline void Add(int x,int y,int z){
e[++tot].v = y;
e[tot].next = head[x];
e[tot].val = z;
head[x] = tot;
}
inline void dfs1(int x,int fa){
b[x][0] = siz[x] = 1;
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(v == fa)continue;
dis[v] = dis[x] + e[i].val;
dfs1(v,x);
siz[x] += siz[v];
for(int j = 0;j <= 15;++j){
int k = (j + e[i].val) % 16;
b[x][k] += b[v][j];
}
}
}
inline void dfs2(int x,int fa){
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(v == fa)continue;
f[v] = f[x] - siz[v] * e[i].val + (n - siz[v]) * e[i].val;;
for(int j = 0;j <= 15;++j){
int k = (j + e[i].val) % 16;
g[v][k] = g[x][j] + b[v][k] - b[v][((j - e[i].val + 16) % 16 + 16) % 16];
}
dfs2(v,x);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i < n;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Add(x,y,z);
Add(y,x,z);
}
dfs1(1,0);
for(int i = 1;i <= n;++i){
f[1] += dis[i];
}
for(int i = 0;i <= 15;++i){
g[1][i] = b[1][i];
}
dfs2(1,0);
for(int i = 1;i <= n;++i){
g[i][0]--;
for(int j = 0;j <= 15;++j){
f[i] += ((j ^ m) - j) * g[i][j];
}
printf("%d\n",f[i]);
}
return 0;
}
D. 放置機器人
分析
二分圖,如果沒有牆的話直接行和列之間建邊,然后匈牙利算法就行了。有牆的話就從牆把某行或某列分開,多幾個標號和節點就行了。
代碼
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 2500+10;
const int maxm = 60;
int m,n;
struct Node{
int v,next;
}e[maxn<<1];
char s[100][100];
int match[maxn];
int vis[maxn];
int bel1[maxm][maxm],cnt1;
int bel2[maxm][maxm],cnt2;
int head[maxn],tot;
inline void Add(int x,int y){
e[++tot].v = y;
e[tot].next = head[x];
head[x] = tot;
}
inline int dfs(int x){
vis[x] = 1;
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(vis[v])continue;
vis[v] = 1;
if(match[v] == -1 || dfs(match[v])){
match[v] = x;
return 1;
}
}
return 0;
}
int main(){
memset(match,-1,sizeof(match));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i){
scanf("%s",s[i]+1);
}
for(int i = 1;i <= n;++i){
for(int j = 1;j <= m;++j){
if(s[i][j] == 'o'){
bel1[i][j] = ++cnt1;
while(s[i][j] == 'o' || s[i][j] == '*'){
bel1[i][j] = cnt1;
j++;
}
}
}
}
cnt2 = cnt1 + 1;
for(int j = 1;j <= m;++j){
for(int i = 1;i <= n;++i){
if(s[i][j] == 'o'){
bel2[i][j] = ++cnt2;
while(s[i][j] == 'o' || s[i][j] == '*'){
bel2[i][j] = cnt2;
i++;
}
}
}
}
int ans = 0;
for(int i = 1;i <= n;++i){
for(int j = 1;j <= m;++j){
if(s[i][j] == 'o'){
Add(bel1[i][j],bel2[i][j]);
}
}
}
for(int i = 1;i <= cnt1;++i){
memset(vis,0,sizeof(vis));
ans += dfs(i);
}
printf("%d\n",ans);
return 0;
}
\(Never\ Give\ Up\)
