2022-2023 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2022)(A~H)组队训练题解
比赛链接:https://codeforces.com/gym/104030
赛后补:E和F
A.Ace Arbiter
思路:仔细一看数据范围最大就是11+10,那就2的22次方得了,显然数量级在1e6,所以直接暴力dfs即可,记得前面给的记录去重下
#include<bits/stdc++.h>
#define endl "\n"
#include<functional>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
void fio(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
pair<ll,ll>f[450];
ll cnt=0;
ll k[450];
void solve()
{
ll n;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
ll l,r;
scanf("%lld-%lld",&l,&r);
if(cnt==0)f[++cnt].first=l,f[cnt].second=r,k[cnt]=cnt;
else if(f[cnt].first==l&&f[cnt].second==r)k[cnt]=i;
else f[++cnt].first=l,f[cnt].second=r,k[cnt]=i;
}
ll ans=0;
function<void(ll,ll,ll,ll)>dfs=[&](ll s,ll js,ll x,ll y)
{
if(s<=cnt&&(x==f[s].first&&y==f[s].second))
{
ans=max(ans,s);
s++;
}
if(s>cnt)
return;
if(x==11||y==11)return ;
if(js==1)
{
dfs(s,js+1,y+1,x);
dfs(s,js+1,y,x+1);
}
else if((js-1)%4==1)
{
dfs(s,js+1,x+1,y);
dfs(s,js+1,x,y+1);
}
else if((js-1)%4==2)
{
dfs(s,js+1,y,x+1);
dfs(s,js+1,y+1,x);
}
else if((js-1)%4==3)
{
dfs(s,js+1,x+1,y);
dfs(s,js+1,x,y+1);
}
else
{
dfs(s,js+1,y,x+1);
dfs(s,js+1,y+1,x);
}
return ;
};
dfs(1,1,0,0);
if(ans!=cnt)
{
printf("error %lld\n",k[ans]+1);
}
else printf("ok\n");
}
signed main()
{
// fio();
ll t;
//cin >> t;
t=1;
while(t--){
solve();
}
return 0;
}
B.Berry Battle
思路:自己手画了一下,树的直径小于等于3必定无解,否则先按照树的直径如1 2 3 4 5 6...,先1 3 4 2,然后5 6...,把树的直径的坐标按我左边这样排即可,然后在另一个树的端点bfs最近没有被用过的点即可,这种显然可保证蚂蚁永远不会同时到同一个点
#include<bits/stdc++.h>
#define endl "\n"
#include<functional>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
void fio(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ef[350000];
bool vis[350000];
bool vi[350000];
vector<ll> g[350000];
void solve()
{
ll n;
cin>>n;
for(ll i=1;i<n;i++)
{
ll l,r;
cin>>l>>r;
g[l].push_back(r);
g[r].push_back(l);
}
// cout<<156<<endl;
ll f=0,id=0;
function<void(ll,ll,ll)>dfs=[&](ll x,ll fa,ll d)
{
if(f<d)f=d,id=x;
for(auto j:g[x])
{
if(j==fa)continue;
dfs(j,x,d+1);
}
};
dfs(1,0,1);
ll id1=0;f=0;
function<void(ll,ll,ll)>df=[&](ll x,ll fa,ll d)
{
if(f<d)f=d,id1=x;
for(auto j:g[x])
{
if(j==fa)continue;
df(j,x,d+1);
}
};
df(id,0,1);
if(f<=3)
cout<<"NO"<<endl;
else
{
vector<ll>ans,op;
ll pd=0;
function<void(ll,ll)>l=[&](ll x,ll fa)
{
op.push_back(x);
if(x==id1)
{
pd=1;
ans=op;
return ;
}
if(pd)return;
for(auto j:g[x])
{
if(j==fa)continue;
l(j,x);
if(pd)
return;
}
op.pop_back();
};
l(id,0);
ll cnt=0;
ef[++cnt]=ans[0],vis[ans[0]]=1;
ef[++cnt]=ans[2],vis[ans[2]]=1;
ef[++cnt]=ans[3],vis[ans[3]]=1;
ef[++cnt]=ans[1],vis[ans[1]]=1;
for(auto j:ans)
{
if(vis[j])continue;
ef[++cnt]=j,vis[j]=1;
}
function<void(ll)>bfs=[&](ll x)
{
queue<ll>f;
f.push(x);
while(!f.empty())
{
ll x=f.front();
f.pop();
if(vi[x])continue;
vi[x]=1;
if(vis[x]==0)
ef[++cnt]=x;
vis[x]=1;
for(auto j:g[x])
{
if(vi[j])continue;
f.push(j);
}
}
};
bfs(id1);
cout<<"YES"<<endl;
for(ll i=1;i<=cnt;i++)
cout<<ef[i]<<" ";
cout<<endl;
}
}
signed main()
{
fio();
ll t;
//cin >> t;
t=1;
while(t--){
solve();
}
return 0;
}
C.Coffee Cup Combo
思路:签到题,不多说了
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
ll t,n,m;
void fio(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
void solve(){
string str;
cin >> n >> str;
ll cnt=0;
ll ans=0;
for(auto i:str){
if(i=='0' && cnt!=0){
cnt--;
ans++;
}
else if(i=='1'){
ans++;
cnt=2;
}
}
cout << ans << endl;
}
signed main()
{
fio();
//cin >> t;
t=1;
while(t--){
solve();
}
return 0;
}
D.Disc District
思路:显然欧几里得距离大于r的一个整数坐标点,那么(r,1)即可
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
ll t,n,m;
void fio(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
void solve(){
ll r;
cin >> r;
cout << r << " " << 1 << endl;
}
signed main()
{
fio();
//cin >> t;
t=1;
while(t--){
solve();
}
return 0;
}
E.Enigmatic Enumeration
思路:首先确定一个最小的环,假定有个点已经被选中了,那么我们从这个点出发进行bfs将很容易统计答案个数(可以建立dis数组,发现dis[x]+dis[y]+1即为次环的大小(x与y不能是父亲与儿子关系),且x必须走过)。于是我们可以暴力所有点作为必须点进行bfs,然后对已经走过点开个vis标记再记录下,表示这个点不需要用,这样就完成了50%。随后可以想到,如果从一个点再延申出来的一个点的父亲其实不是唯一确定的(因为它可能是所有与他相连的点延申出来的),此时我们分两种情况,当前点编号为id,环最短长度len=dis[id]+dis[x]+1,此时x不是id的父亲,但是dis[x]=dis[
注意这里的maxx其实应该是minn,还有每次maxx能减小之前的答案是全作废的
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<functional>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
//#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
//const ll p=rnd()%mod;
const ll N = 205000;
#define F first
#define S second
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s
{
ll l, r, k;
friend bool operator<(const s& a, const s& b)
{
return a.l > b.r;
}
};
bool vi[3500], vis[3500];
ll dis[3500];
vector<ll>g[3000 + 5];
int main()
{
fio();
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
g[l].push_back(r);
g[r].push_back(l);
}
ll maxx = 1e18;
ll ans = 0;
function<void(ll)>bfs = [&](ll x)
{
for (ll j = 1; j <= n; j++)vi[j] = 0;
queue<s>f;
//ll z=x;
f.push({ 0,x,0 });
while (!f.empty())
{
ll x = f.front().l;
ll id = f.front().r;
ll k = f.front().k;
f.pop();
if (vi[id] || vis[id])continue;
vi[id] = 1;
dis[id] = x;
ll cnt = 0;
ll cn1 = 0;
for (auto j : g[id])
{
if (vi[j] && j != k)
{
if (maxx > dis[j] + dis[id] + 1)maxx = dis[j] + dis[id] + 1, cn1 = 1, ans = 0;
else if (maxx == dis[j] + dis[id] + 1)cn1++;
}
if (vi[j] && dis[j] == dis[id] - 1)cnt++;
}
if (dis[k] + dis[id] + 1 == maxx)
{
ans += cnt * (cnt - 1) / 2;
}
else
{
ans += cn1 * cnt;
}
for (auto j : g[id])
{
if (vi[j] || vis[j])continue;
f.push({ x + 1,j,id });
}
}
};
for (int i = 1; i <= n; i++)
{
bfs(i);
vis[i] = 1;
}
//bfs(1);
cout << ans << endl;
}
F.Foreign Football
思路::其实我们就是去找合理答案个数,什么时候有合理答案?我们不妨选定一个字符串并枚举前缀的所有可能,显然只要这个长度固定,那么所有答案的长度留固定了,而固定了长度的字符串,那么它的hash值是唯一的,所以开了二维数组pre[i][j],表示第i 个队伍的名字长为j(从前往后)出现次数,显然答案出现时,其pre[i][j]为n-1,那么我们也同时建立个后缀sub[i][j]表示第i个队伍长为j(从后往前)出现次数,显然答案出现时,其sub也为n-1,那么后面我们只需找最短的字符串去爆枚即可(理论上是1e6/n(枚举答案) * (O(n)(问下对应出现次数是否为n-1)+n * n(看下字符串长度是否符合长度标准))。当然我直接枚举了1和2构成的一个字符串也可以过,因为合法字符串有对称性,不可能一个字符串太长且n很大。所以前面的n方检测还是有必要的。
#include<vector>
#include<map>
#include<iostream>
#define endl "\n"
#include<functional>
#include<random>
#include<time.h>
using namespace std;
typedef long long ll;
mt19937 rnd(time(0));
const ll mod = 1e9 + 7;
const ll p = rnd() % mod;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 5;
void fio() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)ans = (x * ans) % mod;
x = (x * x) % mod;
y >>= 1;
}
return ans % mod;
}
vector<ll>pre[501];
vector<ll>sub[501];
vector<ll>sj[501];
ll k[501][501];
ll d[501];
string h[501][501];
void solve()
{
ll n;
cin >> n;
string f;
ll e = ksm(p, mod - 2);
map<ll, ll>o;
ll cnt = 0;
for (ll i = 1;i <= n;i++)
{
for (ll j = 1;j <= n;j++)
{
cin >> h[i][j];
d[i] = max(d[i], (ll)h[i][j].size());
}
pre[i].resize(d[i] + 4, 0);
sub[i].resize(d[i] + 4, 0);
sj[i].resize(d[i] + 4, 0);
}
ll cd = 0;
for (ll i = 1;i <= n;i++)
{
for (ll j = 1;j <= n;j++)
{
if (h[i][j].size() != h[j][i].size())cd = 1;
}
}
if (cd)
{
cout << "NONE" << endl;
return;
}
for (ll i = 1;i <= n;i++)
{
for (ll j = 1;j <= n;j++)
{
f = h[i][j];
if (i != j)
{
ll hash = 0;
for (ll s = 0;s < f.size();s++)
{
hash = ((hash * p) % mod + f[s]) % mod;
}
ll co = 0;
ll cs = 0;
for (ll s = f.size() - 1;s >= 1;s--)
{
hash -= f[s];
if (hash < 0)hash += mod;
hash = (hash * e) % mod;
co = co + ((f[s] * ksm(p, cs)) % mod);
if (co >= mod) co -= mod;
if (sj[i][s] == hash) pre[i][s]++;
else sub[i][s] = 0, pre[i][s] = 0, sj[i][s] = hash, pre[i][s]++;
//
if (sj[j][f.size() - s] ==co)sub[j][f.size() - s]++;
else sub[j][f.size() - s] = 0, pre[j][f.size() - s] = 0, sj[j][f.size() - s] = co, sub[j][f.size() - s]++;
cs++;
}
k[i][j] = f.size();
}
}
}
cnt = 0;
ll of = 0;
for (ll i = 1;i < k[1][2];i++)
{
ll pd = 0;
if (pre[1][i] == sub[1][i] && pre[1][i] == n - 1)
{
for (ll j = 2;j <= n;j++)
{
if ((k[j][1] - i) <= 0)pd = 1;
else
{
if (pre[j][k[j][1] - i] == sub[j][k[j][1] - i] && pre[j][k[j][1] - i] == n - 1)
{
continue;
}
else pd = 1;
}
if (pd)break;
}
for(ll j=2;j<=n;j++)
{
for(ll d=2;d<=n;d++)
{
if(j==d)continue;
if(k[j][1]-i+k[d][1]-i!=k[j][d])pd=1;
}
}
if (pd == 0)
cnt++, of = i;
if (cnt >= 2)break;
}
}
if (cnt >= 2)
cout << "MANY" << endl;
else if (cnt == 0)
cout << "NONE" << endl;
else
{
cout << "UNIQUE" << endl;
cout << h[1][2].substr(0, of) << endl;
for (ll j = 2;j <= n;j++)
{
cout << h[j][1].substr(0, k[j][1] - of) << endl;
}
}
}
signed main()
{
fio();
ll t;
t = 1;
while (t--) {
solve();
}
return 0;
}
G.Graduation Guarantee
思路:队友写的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rii int
const int N=3e5;
inline rii read() {
rii x = 0, f = 0;
char c = getchar();
while (c > 57 || c < 48) {
f |= c == 45;
c = getchar();
}
while (c <= 57 && c >= 48) {
x = (x << 3) + (x << 1) + c - 48;
c = getchar();
}
return f ? -x : x;
}
double ff[5005];
double a[5002][10005]={0};
bool cmp(double& x,double& y){
return x>y;
}
void solve(){
int n,k;
n=read();
k=read();//初始5002分
for(int i=1;i<=n;i++){
scanf("%lf",&ff[i]);
}
sort(ff+1,ff+1+n,cmp);
a[0][5002]=1;
for(int i=1;i<=n;i++){
for(int j=5002-i;j<=5002+i;j++){
a[i][j]=a[i-1][j-1]*ff[i]+a[i-1][j+1]*(1-ff[i]);
}
}
double res=0;
for(int j=1;j<=n;j++){
double ans=0;
for(int i=5002+k;i<=10004;i++){
ans+=a[j][i];
}
res=max(res,ans);
}
printf("%.6lf",res);
}
int main(){
int t=1;
// t=read();
while(t--){
solve();
// printf("\n");
}
return 0;
}
H.Highest Hill
思路:队友写的,应该不太难
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rii ll
const int N=3e5;
inline rii read() {
rii x = 0, f = 0;
char c = getchar();
while (c > 57 || c < 48) {
f |= c == 45;
c = getchar();
}
while (c <= 57 && c >= 48) {
x = (x << 3) + (x << 1) + c - 48;
c = getchar();
}
return f ? -x : x;
}
ll a[N];
ll r[N];//往右小于
ll l[N];//往左小于
void solve(){
ll n;
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
l[1]=0;
for(int i=2;i<=n;i++){
if(a[i]>=a[i-1]){
l[i]=l[i-1]+1;
}else{
l[i]=0;
}
}
r[n]=0;
for(int i=n-1;i>=1;i--){
if(a[i]>=a[i+1]){
r[i]=r[i+1]+1;
}else{
r[i]=0;
}
}
ll ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,min(a[i]-a[i-l[i]],a[i]-a[i+r[i]]));
}
printf("%lld",ans);
}
int main(){
int t=1;
// t=read();
while(t--){
solve();
printf("\n");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】