The 3rd Universal Cup. Stage 7: Warsaw 补题
A
太牛了。
复读 jiangly 题解。
先把代价除以二。
设 \(f_{i,j}\) 表示以 \(j\) 的代价覆盖前 \(i\) 个点最多还能覆盖多少距离。发现只有 \(f_{i,x},f_{i,x+1},f_{i,x+2}\) 的值是有意义的。其中 \(x\) 为覆盖的最小代价。因为 \(f_{i,x+3}\) 一定不优。不如你到下个点再买一张短时票。
类似于 ddp 一样的,把 \(f_{i,x},f_{i,x+1},f_{i,x+2}\) 计入 dp 状态。设 \(dp_{i,A,B,C}\) 表示当前考虑到了 \(a_i\), \(f_{i,x} = A,f_{i,x+1} = B,f_{i,x+2} = C\) 的方案数。统计答案计数考虑只在每次 \(x\) 增加的时候统计答案。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int mininf = 1e9 + 7;
#define int long long
#define pb emplace_back
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');}
#define put() putchar(' ')
#define endl puts("")
const int MAX = 1e3 + 10;
const int mod = 1e9 + 7;
int a[MAX];
int f[2][80][80][80];
int g[10];
int pre[MAX];
void solve(){
int n = read();
pre[0] = 1;
for(int i = 1; i <= n; i++){
a[i] = read();
pre[i] = pre[i - 1] * 2 % mod;
}
f[0][0][0][0] = 1;
int ans = 0;
for(int i = 1; i <= n; i++){
for(int A = 0; A <= 75; A++) for(int B = 0; B <= 75; B++) for(int C = 0; C <= 75; C++) f[i & 1][A][B][C] = 0;
for(int A = 0; A <= 75; A++){
for(int B = A; B <= 75; B++){
for(int C = B; C <= 75; C++){
if(!f[(i & 1) ^ 1][A][B][C]) continue;
g[1] = max(0ll, A - (a[i] - a[i - 1])), g[2] = max(0ll, B - (a[i] - a[i - 1])), g[3] = max(0ll, C - (a[i] - a[i - 1])), g[4] = g[5] = g[6] = 0;
for(int j = 1; j <= 6; j++){
if(j + 1 <= 6) g[j + 1] = max(g[j + 1], g[j] + 20);
if(j + 3 <= 6){
g[j + 3] = max(g[j + 3], g[j] + 75);
}
g[j] = max(g[j], g[j - 1]);
}
f[i & 1][g[1]][g[2]][g[3]] = (f[i & 1][g[1]][g[2]][g[3]] + f[(i & 1) ^ 1][A][B][C]) % mod;
int now = 1;
while(!g[now]) now++;
ans = (ans + f[(i & 1) ^ 1][A][B][C] * pre[n - i] % mod * (now - 1) % mod) % mod;
f[i & 1][g[now]][g[now + 1]][g[now + 2]] = (f[i & 1][g[now]][g[now + 1]][g[now + 2]] + f[(i & 1) ^ 1][A][B][C]) % mod;
}
}
}
}
write(ans * 2 % mod), endl;
}
signed main(){
int t = 1;
while(t--) solve();
return 0;
}
B
简单题。直接钦定左边 ? 或者右边 ? 的都为 \(l=r\) 的节点。然后判断能不能放得下,可不可以填完就好了。
赛时糖丸了,贡献一车罚时。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int mininf = 1e9 + 7;
#define int long long
#define pb emplace_back
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');}
#define put() putchar(' ')
#define endl puts("")
const int MAX = 4e5 + 10;
struct node{
int l, r;
int id;
}; node a[MAX];
bool cmp(node x, node y){
return x.l < y.l;
}
void solve(){
int n = read(), L = read();
int n2 = 0;
int cnt2 = 0;
for(int i = 1; i <= n; i++){
int l = read(), r = read();
if(l == -1 and r == -1){
cnt2++;
continue;
}
n2++;
if(l == -1) l = r, a[n2].id = 1;
else if(r == -1) r = l, a[n2].id = 2;
else a[n2].id = 3;
a[n2].l = l, a[n2].r = r;
}
if(!n2){
if(L<cnt2)puts("NIE");
else puts("TAK");
return ;
}
sort(a + 1, a + n2 + 1, cmp);
int nowr = 0;
int cnt4 = 0;
int lft = L;
if(a[1].l != 1 and a[1].id != 1){
cnt4++;
}
for(int i = 1; i <= n2; i++){
if(a[i].l == a[i - 1].l or a[i].r == a[i - 1].r){
puts("NIE");
return ;
}
if(a[i].l <= nowr){
puts("NIE");
return ;
}
if(a[i].id == 3 or a[i].id == 2){
if(a[i - 1].id == 2){
}else if(a[i - 1].id == 3 or a[i - 1].id == 1){
if(a[i - 1].r != a[i].l - 1){
cnt4++;
}
}
}else{
}
lft -= a[i].r - a[i].l + 1;
nowr = a[i].r;
}
if(a[n2].r != L and a[n2].id != 2){
cnt4++;
}
if(cnt4 > cnt2){
puts("NIE");
return ;
}
if(lft < cnt2){
puts("NIE");
return ;
}
puts("TAK");
return ;
}
signed main(){
int t = read();
while(t--) solve();
return 0;
}
C
还不会
D
ucup 场上过的第一题,感动。
这个直接挨个判断能不能成为中位数就行了吧。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int mininf = 1e9 + 7;
#define int long long
#define pb emplace_back
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');}
#define put() putchar(' ')
#define endl puts("")
const int MAX = 2e5 + 10;
int a[MAX];
map <int, int> mk;
void solve(){
int n = read(), k = read(), m = read();
for(int i = 1; i <= n; i++){
a[i] = read();
}
sort(a + 1, a + n + 1);
if(k % 2){
bool fl = 0;
for(int i = 1; i <= n; i++){
if(a[i] == m){
if(i - 1 >= k / 2 and n - i >= k / 2){
fl = 1;
break;
}
}
}
if(fl){
puts("TAK");
}else{
puts("NIE");
}
}else{
bool fl = 0;
for(int i = 1; i <= n; i++){
if(mk.count(2 * m - a[i])){
// write(i), endl;
if(mk[2 * m - a[i]] - 1 >= k / 2 - 1 and n - i >= k / 2 - 1){
fl = 1;
break;
}
}
mk[a[i]] = i;
}
if(fl){
puts("TAK");
}else{
puts("NIE");
}
}
mk.clear();
}
signed main(){
int t = read();
while(t--) solve();
return 0;
}
E
金钩爷跟我说这题存在最短路做法,膜拜。
但是可以直接看路径存在的条件,发现相邻两个村庄距离不能小于 2。然后直接最小割。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int mininf = 1e9 + 7;
#define int long long
#define pb emplace_back
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');}
#define put() putchar(' ')
#define endl puts("")
const int MAX = 6e3 + 10;
int psz = 2;
struct flow{
struct node{
int v, w, cp;
}; vector <node> g[MAX];
int dis[MAX];
bool bfs(int s, int t){
for(int i = 1; i <= psz; i++) dis[i] = mininf;
dis[s] = 0;
queue <int> q;
q.push(s);
while(!q.empty()){
int u = q.front();
q.pop();
for(auto V : g[u]){
if(V.w > 0 and dis[V.v] > dis[u] + 1){
dis[V.v] = dis[u] + 1;
q.push(V.v);
}
}
}
if(dis[t] == mininf) return 0;
return 1;
}
int cur[MAX];
int aug(int u, int now, int t){
if(u == t) return now;
int ans = 0;
for(int &i = cur[u]; i < g[u].size(); i++){
int v = g[u][i].v, w = g[u][i].w, cp = g[u][i].cp;
if(dis[v] != dis[u] + 1) continue;
int ret = aug(v, min(w, now), t);
g[u][i].w -= ret, g[v][cp].w += ret;
now -= ret, ans += ret;
if(now <= 0) break;
}
return ans;
}
void add_edge(int u, int v, int w){
g[u].pb(node{v, w, g[v].size()});
g[v].pb(node{u, 0, g[u].size() - 1});
}
}; flow G;
int id[55][55][2];
int a[55][55];
void solve(){
int n = read(), m = read();
int s = 1, t = 2;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
char ch = getchar();
while(ch != '.' and ch != '#'){
ch = getchar();
}
if(ch == '#'){
a[i][j] = 1;
id[i][j][0] = ++psz, id[i][j][1] = ++psz;
}
}
}
for(int i = 1; i <= m; i++) if(a[1][i]) G.add_edge(id[1][i][1], t, inf);
for(int i = 1; i <= n; i++) if(a[i][1]) G.add_edge(s, id[i][1][0], inf);
for(int i = 1; i <= m; i++) if(a[n][i]) G.add_edge(s, id[n][i][0], inf);
for(int i = 1; i <= n; i++) if(a[i][m]) G.add_edge(id[i][m][1], t, inf);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(!a[i][j]) continue;
G.add_edge(id[i][j][0], id[i][j][1], 1);
for(int k = max(1ll, i - 2); k <= min(n, i + 2); k++){
for(int k2 = max(1ll, j - 2); k2 <= min(m, j + 2); k2++){
if(k == i and k2 == j) continue;
if(a[k][k2]){
G.add_edge(id[i][j][1], id[k][k2][0], inf);
}
}
}
}
}
int ans = 0;
while(G.bfs(s, t)){
for(int i = 1; i <= psz; i++) G.cur[i] = 0;
ans += G.aug(s, inf, t);
}
write(ans), endl;
}
signed main(){
int t = 1;
while(t--) solve();
return 0;
}
F
这题是真的垃圾。考虑到斐波那契数列增长很快啊。所以我们对于每一个数 \(a\) 只需要枚举比它大一个的斐波那契数即可。
然后你要估算对应的数是斐波那契数第几项。。。。。
G
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int mininf = 1e9 + 7;
#define int long long
#define pb emplace_back
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');}
#define put() putchar(' ')
#define endl puts("")
const int MAX = 55;
int a[MAX][MAX];
void solve(){
int n = read();
int ans = 0;
for(int i = 1; i <= n; i++){
int cnt = inf;
for(int j = 1; j <= n; j++){
a[i][j] = read();
cnt = min(cnt, a[i][j]);
}
ans = max(ans, cnt);
}
write(ans), endl;
}
signed main(){
int t = 1;
while(t--) solve();
return 0;
}
HI 不会
J
直接 dp 就好了。
L
随机的话,发现其实能拼成的概率不大,所以只需要枚举一些就好了。
G
直接做