Preface
闲了两天没训练,今天又开始上班,结果唐得发昏后期也没题可写直接光速下班
只能说感觉老外的题目难度跨度都好大,easy确实简单,hard确实难,medium确实少
A. A-Mazing Puzzle
题目看起来很复杂,但仔细一想会发现有用的状态总数只有\(4n^2\)种
即我们可以暴力记录下两个机器人的坐标,然后记下其中一个机器人的方向,就可以唯一确定一个状态了(因为两个机器人的方向的相对差距是不变的,因此记一个就行)
然后转移的话就直接写一个类似Dijkstra的东西即可,要注意各种细节
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
#define ft first
#define sd second
const int N = 55;
const int dir1[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
const int dir2[4][3] = {{0, 0, 0}, {1, 0, 0}, {0, 0, -1}, {1, -1, 0}};
const char pat[5] = "NESW";
int n, c, r, e;
int c1, r1, d1, c2, r2, d2;
bool wall[2][55][55];
int dis[52][52][52][52][2];
bool inD[52][52][52][52];
struct Node{
int cc1, rr1, cc2, rr2;
pii d;
bool operator<(const Node &b)const{return d > b.d;}
};
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
memset(dis, 0x3f, sizeof(dis));
char ch1, ch2;
cin >> c >> r >> e;
cin >> c1 >> r1 >> ch1 >> c2 >> r2 >> ch2;
for (int i=0; i<4; ++i){
if (ch1==pat[i]) d1=i;
if (ch2==pat[i]) d2=i;
}
cin >> n;
for (int i=1; i<=n; ++i){
int c0, r0; cin >> c0 >> r0;
wall[0][c0][r0]=true;
}
for (int i=1; i<=c; ++i) wall[0][i][0]=wall[0][i][r]=true;
for (int i=1; i<=r; ++i) wall[1][0][i]=wall[1][c][i]=true;
wall[0][e][0]=false;
cin >> n;
for (int i=1; i<=n; ++i){
int c0, r0; cin >> c0 >> r0;
wall[1][c0][r0]=true;
}
priority_queue<Node> que;
que.push(Node{c1, r1, c2, r2, make_pair(0, 0)});
dis[c1][r1][c2][r2][0]=0, dis[c1][r1][c2][r2][1]=0;
while (!que.empty()){
auto [cc1, rr1, cc2, rr2, pp] = que.top(); que.pop();
// printf("cc1=%d rr1=%d cc2=%d rr2=%d pp(%d %d)\n", cc1, rr1, cc2, rr2, pp.ft, pp.sd);
if (inD[cc1][rr1][cc2][rr2]) continue;
inD[cc1][rr1][cc2][rr2]=true;
for (int j=0; j<4; ++j){
int bp=0;
int nc1, nr1;
auto [a1, b1, c1] = dir2[(j+d1)%4];
if (wall[a1][cc1+b1][rr1+c1]) nc1=cc1, nr1=rr1, ++bp;
else if (cc1==e && rr1==0) nc1=cc1, nr1=rr1;
else nc1=cc1+dir1[(j+d1)%4][0], nr1=rr1+dir1[(j+d1)%4][1];
int nc2, nr2;
auto [a2, b2, c2] = dir2[(j+d2)%4];
if (wall[a2][cc2+b2][rr2+c2]) nc2=cc2, nr2=rr2, ++bp;
else if (cc2==e && rr2==0) nc2=cc2, nr2=rr2;
else nc2=cc2+dir1[(j+d2)%4][0], nr2=rr2+dir1[(j+d2)%4][1];
if (bp==2) continue;
if (nc1==nc2 && nr1==nr2 && !(nc1==e && nr1==0)) continue;
pii npp = make_pair(pp.ft+1, pp.sd+bp);
// printf("nc1=%d nr1=%d nc2=%d nr2=%d, npp(%d %d)\n", nc1, nr1, nc2, nr2, npp.ft, npp.sd);
if (npp < make_pair(dis[nc1][nr1][nc2][nr2][0], dis[nc1][nr1][nc2][nr2][1])){
que.push(Node{nc1, nr1, nc2, nr2, npp});
dis[nc1][nr1][nc2][nr2][0]=npp.ft;
dis[nc1][nr1][nc2][nr2][1]=npp.sd;
// if ((nc1==e && nr1==0) && (nc2==e && nr2==0)){
// printf("FUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
// }
}
}
// puts("");
}
cout << dis[e][0][e][0][0] << ' ' << dis[e][0][e][0][1] << '\n';
return 0;
}
B. A Musical Question
签到,直接暴力DP即可,二元组\((x,y)\)表示第一张唱片刻录长度为\(x\)的歌,第二张唱片刻录长度为\(y\)的歌的状态
#include<cstdio>
#include<iostream>
#include<vector>
#include<utility>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=1005;
int n,m; bool vis[N][N];
int main()
{
RI i; vector <pi> S; S.push_back(pi(0,0)); vis[0][0]=1;
for (scanf("%d%d",&m,&n),i=1;i<=n;++i)
{
int w; scanf("%d",&w); vector <pi> NS;
for (auto [x,y]:S)
{
if (x+w<=m&&!vis[x+w][y]) vis[x+w][y]=1,NS.push_back(pi(x+w,y));
if (y+w<=m&&!vis[x][y+w]) vis[x][y+w]=1,NS.push_back(pi(x,y+w));
}
for (auto it:NS) S.push_back(it);
}
int ans1=0,ans2=0; for (auto [x,y]:S)
if (x+y>ans1+ans2||(x+y==ans1+ans2&&abs(x-y)<abs(ans1-ans2))) ans1=x,ans2=y;
return printf("%d %d",max(ans1,ans2),min(ans1,ans2)),0;
}
C. Cribbage On Steroids
首先pair
的贡献很好算,然后顺子的话只要枚举顺子的起点和终点即可快速算贡献
至于15's的话可以直接大力DP,设\(f_{i,j}\)表示用前\(i\)种点数的牌组成价值和为\(j\)的牌面的方案数,转移很trivial
#include<bits/stdc++.h>
using namespace std;
#define int long long
int C[105][105];
int n, cnt[20];
int sum[20];
int f[20][20];
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
C[0][0]=C[1][0]=C[1][1]=1;
for (int i=2; i<105; ++i){
C[i][0]=C[i][i]=1;
for (int j=1; j<i; ++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
}
// for (int i=0; i<=100; ++i) printf("C[100][%d]=%lld\n", i, C[100][i]);
cin >> n;
for (int i=1; i<=n; ++i){
char ch; cin >> ch;
if ('T'==ch) ++cnt[10];
else if ('J'==ch) ++cnt[11];
else if ('Q'==ch) ++cnt[12];
else if ('K'==ch) ++cnt[13];
else if ('A'==ch) ++cnt[1];
else ++cnt[ch-'0'];
}
int ans=0;
for (int i=1; i<=13; ++i){
sum[i] += sum[i-1]+i*cnt[i];
}
for (int i=1; i<=13; ++i){
if (cnt[i]>=2) ans += cnt[i]*(cnt[i]-1);
}
// printf("ans=%lld\n", ans);
int lst=0, res=1;
for (int i=1; i<=14; ++i){
if (0==cnt[i]){
if (i-lst-1>=3) ans += res*(i-lst-1);
res=1; lst=i;
}else res*=cnt[i];
}
// printf("ans=%lld\n", ans);
cnt[10]+=cnt[11];
cnt[10]+=cnt[12];
cnt[10]+=cnt[13];
f[0][0]=1;
for (int i=1; i<=10; ++i){
for (int j=0; j<=15; ++j){
for (int k=0; k<i; ++k){
for (int w=1; w<=cnt[i]; ++w){
if (w*i>j) break;
f[i][j] += f[k][j-w*i]*C[cnt[i]][w];
}
}
}
}
for (int i=1; i<=10; ++i) ans += f[i][15]*2;
cout << ans << '\n';
return 0;
}
D. Determining Nucleotide Assortments
签到,注意不要搞错ATGC
的顺序
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+5;
using pii = pair<int, int>;
#define ft first
#define sd second
int n, q;
int cnt[4][N];
char pat[5] = "ATGC";
char str[N];
signed main(){
scanf("%s", str+1);
n = strlen(str+1);
for (int i=1; i<=n; ++i){
for (int j=0; j<4; ++j){
if (str[i]==pat[j]) ++cnt[j][i];
}
}
for (int i=1; i<=n; ++i){
for (int j=0; j<4; ++j){
cnt[j][i] += cnt[j][i-1];
}
}
scanf("%d", &q);
while (q--){
int l, r; scanf("%d%d", &l, &r);
vector<pii> vec;
for (int j=0; j<4; ++j) vec.push_back(make_pair(cnt[j][r]-cnt[j][l-1], j));
sort(vec.begin(), vec.end(), [&](pii a, pii b){return a.ft!=b.ft ? a.ft>b.ft : a.sd<b.sd;});
for (auto [a, b] : vec) putchar(pat[b]);
puts("");
}
return 0;
}
E. Hilbert's Hedge Maze
因为前面我太唐了没给徐神留出足够的时间,红豆泥私密马赛
这题徐神给我讲了做法我也没太懂,只能说笨比脑子是这样的,只能坐等徐神补题了
F. It's About Time
大力枚举+小推式子,对于徐神来说就是洒洒水
#include <bits/stdc++.h>
int main() {
const long double pi = std::acos(-1.L);
long double r, s, h;
std::cin >> r >> s >> h;
const long double tyear = 2.L * pi * r / (s * h);
const long double cy = std::round(tyear);
const long double ly = cy < tyear ? cy + 1.L : cy - 1.L;
long double mindelta = 1e9;
std::array<int, 3> ans;
for(int n1 = 2; n1 < 1000; ++n1) {
for(int n2 = n1 + n1; n2 < 1000; n2 += n1) {
for(int n3 = n2 + n2; n3 <= 1000; n3 += n2) {
const int l = n3 / n1 - n3 / n2 + n3 / n3;
const int c = n3 - l;
const long double aver = (l * ly + c * cy) / (long double)n3;
if(std::abs(aver - tyear) < mindelta) {
mindelta = std::abs(aver - tyear);
ans = {n1, n2, n3};
}
}
}
}
std::cout << ans[0] << ' ' << ans[1] << ' ' << ans[2] << std::endl;
return 0;
}
G. Pea Pattern
这种乍一看不知道有什么好做法,但是一看榜被过穿了的题,首先考虑大力出奇迹
直觉告诉我们这个序列做到后面很快就会产生重复,因此如果有解的话直接暴力模拟即可
#include<cstdio>
#include<iostream>
#include<set>
#include<string>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
string n,m;
int main()
{
RI i; cin>>n>>m; set <string> ext;
if (n.size()>100||m.size()>100) return puts("I'm bored."),0;
for (int cnt=1;;++cnt)
{
if (n==m) return printf("%d",cnt),0;
if (ext.count(n)) break; ext.insert(n);
static int c[10]; memset(c,0,sizeof(c));
for (i=0;i<n.size();++i) ++c[n[i]-'0'];
string t=""; for (i=0;i<10;++i) if (c[i]) t+=to_string(c[i])+char('0'+i); n=t;
}
return puts("Does not appear"),0;
}
H. Picking Up Steam
好难的几何,大致有个思路但貌似细节很多不太好写
I. Road To Savings
祁神写的,我题目都没看
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int INF = (int)1e8+5;
const int N = 205;
int n, m, src, dr;
int dis[N][N];
struct Edge{int a, b, w;};
vector<Edge> G;
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m >> src >> dr;
for (int i=1; i<=n; ++i){
for (int j=i+1; j<=n; ++j){
dis[i][j] = dis[j][i] = INF;
}
}
for (int i=1; i<=m; ++i){
int a, b, w; cin >> a >> b >> w;
dis[a][b] = dis[b][a] = w;
G.push_back(Edge{a, b, w});
}
for (int k=1; k<=n; ++k){
for (int i=1; i<=n; ++i){
for (int j=1; j<=n; ++j){
if (dis[i][k]+dis[k][j]<dis[i][j]) dis[j][i]=dis[i][j] = dis[i][k]+dis[k][j];
}
}
}
int dist = dis[src][dr];
int ans=0;
//
// for (int i=1; i<=n; ++i){
// for (int j=1; j<=n; ++j){
// printf("%d ", dis[i][j]);
// }puts("");
// }
for (auto [a, b, w] : G){
// printf("(%d %d) %d %d\n", a, b, dis[src][a]+dis[b][dr], dis[src][b]+dis[a][dr]);
if (dis[src][a]+dis[b][dr]+w > dist && dis[src][b]+dis[a][dr]+w > dist) ans += w;
}
cout << ans;
return 0;
}
J. Simple Solitaire
徐神写的,我题目都没看(怎么又是打牌啊)
#include <bits/stdc++.h>
bool shrink(std::vector<std::string> &s) {
// std::cerr << "Before: ";
// for(int i = 0; i < s.size(); ++i) std::cerr << s[i] << char(i == s.size() - 1 ? 10 : 32);
// std::cerr << std::flush;
int pos = -1, type = 0;
for(int i = s.size() - 1; i >= 3; --i) {
if(s[i][0] == s[i - 3][0] && type < 4)
pos = i, type = 4;
if(s[i][1] == s[i - 3][1] && type < 2)
pos = i, type = 2;
}
if(type == 4)
s.erase(s.begin() + (pos - 3), s.begin() + (pos + 1));
if(type == 2)
s.erase(s.begin() + (pos - 3)),
s.erase(s.begin() + (pos - 1));
// std::cerr << "After: ";
// for(int i = 0; i < s.size(); ++i) std::cerr << s[i] << char(i == s.size() - 1 ? 10 : 32);
// std::cerr << std::flush;
return type > 0;
}
int main() {
std::vector<std::string> deck(52), st;
for(auto &card: deck) std::cin >> card;
for(auto card: deck) {
st.push_back(card);
while(shrink(st));
}
std::cout << st.size();
for(int i = 0; i < st.size(); ++i) std::cout << char(32) << st[i];
std::cout << std::endl;
return 0;
}
K. Two Charts Become One
因为没判两个树都是单点的Corner Case红温了好久,浪费了好多时间
根据题目中对于树的同构的定义,其实只要判每个点的儿子按顺序重排后是否相同即可
更方便的写法是直接把每条边按照父亲到儿子的顺序加入集合中,最后比较两个数对应的边集合是否相同即可
然后注意下模拟建树的过程即可,由于只有一个点的时候不存在边因此可以假设一个编号为\(0\)的虚点套在最外层
#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<stack>
#include<set>
#include<cctype>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
string A,B;
inline vector <pi> solve(string s)
{
RI i,j; string t=""; vector <pi> edges;
for (i=0;i<s.size();++i) if (!isspace(s[i])) t+=s[i];
t="(0("+t+"))"; stack <int> stk; set <pi> rst;
for (i=0;i<t.size();++i)
{
if (t[i]=='(') { stk.push(i); continue; }
if (t[i]==')')
{
int pos=stk.top(); stk.pop();
int x=0; for (j=pos+1;;++j)
if (isdigit(t[j])) x=x*10+t[j]-'0'; else break;
for (auto it=rst.upper_bound(pi(pos,0));it!=rst.end();it=rst.erase(it))
edges.push_back(pi(x,it->se));
rst.insert(pi(pos,x)); continue;
}
}
sort(edges.begin(),edges.end());
return edges;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
getline(cin,A); getline(cin,B);
return puts(solve(A)==solve(B)?"Yes":"No"),0;
}
L. Which Warehouse?
很裸的一个题
由于每个点不能选两种及以上的物资,因此我们可以把问题转化为一个二分图最小权匹配问题
左边\(n\)个点代表原图中的点,右边\(m\)个点代表物资,两点间连边对应将所有该类物资移动到对应点上的代价和,这个可以预处理最短路后快速计算
然后直接上KM即可,注意KM要求二分图两边点的数量相同,这点也很好解决,直接在右边多开\(n-m\)个点,表示对应点不作为存储地点,与左边所有点的边权都是\(0\)即可
#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=105,INF=1e18;
int n,m,c[N][N],dis[N][N];
namespace KM
{
int w[N][N],kx[N],ky[N],py[N],vy[N],slk[N],pre[N];
inline int solve(CI n)
{
RI i,j,k; for (i=1;i<=n;++i) for (j=1;j<=n;++j) kx[i]=max(kx[i],w[i][j]);
for (i=1;i<=n;++i)
{
for (j=0;j<=n;++j) vy[j]=pre[j]=0,slk[j]=INF;
int k=0,p=-1; for (py[k=0]=i;py[k];k=p)
{
int d=INF; vy[k]=1; int x=py[k];
for (j=1;j<=n;++j) if (!vy[j])
{
int t=kx[x]+ky[j]-w[x][j];
if (t<slk[j]) slk[j]=t,pre[j]=k;
if (slk[j]<d) d=slk[j],p=j;
}
for (j=0;j<=n;++j)
if (vy[j]) kx[py[j]]-=d,ky[j]+=d; else slk[j]-=d;
}
for (;k;k=pre[k]) py[k]=py[pre[k]];
}
int ret=0; for (i=1;i<=n;++i) ret+=kx[i]+ky[i];
return -ret;
}
};
using namespace KM;
signed main()
{
RI i,j,k; for (scanf("%lld%lld",&n,&m),i=1;i<=n;++i)
for (j=1;j<=m;++j) scanf("%lld",&c[i][j]);
for (i=1;i<=n;++i) for (j=1;j<=n;++j)
if (scanf("%lld",&dis[i][j]),dis[i][j]==-1) dis[i][j]=INF;
for (k=1;k<=n;++k) for (i=1;i<=n;++i) for (j=1;j<=n;++j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for (i=1;i<=n;++i) for (j=1;j<=m;++j)
{
int cur=0; for (k=1;k<=n;++k) cur+=dis[k][i]*c[k][j];
w[i][j]=-cur;
}
return printf("%lld",solve(n)),0;
}
Postscript
每天被花式腐乳,真是菜菜又鸡鸡啊
标签:std,cnt,America,Central,int,2022,const,include,define From: https://www.cnblogs.com/cjjsb/p/18003700