Preface
最近事情挺多积压了三天没训练,本来计划着去补CF的题,但后面还是溜去写WA2杂谈了
今天重归训练结果碰上超级毒瘤场,2h30min9题后剩下题目都难得飞起,最后还是靠徐神大力切了L题(我早就提前下班了)
评价是这就是Russia场的威力吗,后面的Hard题是一点不可做啊
A. Archivist
纯签到
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
int y; scanf("%d",&y);
switch (y)
{
case 1995:
puts("ITMO"); break;
case 1996:
puts("SPbSU"); break;
case 1997:
puts("SPbSU"); break;
case 1998:
puts("ITMO"); break;
case 1999:
puts("ITMO"); break;
case 2000:
puts("SPbSU"); break;
case 2001:
puts("ITMO"); break;
case 2002:
puts("ITMO"); break;
case 2003:
puts("ITMO"); break;
case 2004:
puts("ITMO"); break;
case 2005:
puts("ITMO"); break;
case 2006:
puts("PetrSU, ITMO"); break;
case 2007:
puts("SPbSU"); break;
case 2008:
puts("SPbSU"); break;
case 2009:
puts("ITMO"); break;
case 2010:
puts("ITMO"); break;
case 2011:
puts("ITMO"); break;
case 2012:
puts("ITMO"); break;
case 2013:
puts("SPbSU"); break;
case 2014:
puts("ITMO"); break;
case 2015:
puts("ITMO"); break;
case 2016:
puts("ITMO"); break;
case 2017:
puts("ITMO"); break;
case 2018:
puts("SPbSU"); break;
case 2019:
puts("ITMO"); break;
}
return 0;
}
B. Bicycle
徐神开场写的签到,我没看题目
a = int(input())
x = int(input())
b = int(input())
y = int(input())
a = [a, b]
x = [x, y]
b = [30, 45]
T = int(input())
ans = []
for i in [0, 1]:
upd = a[i]
if T >= b[i]:
upd += (T - b[i]) * x[i] * 21
ans += [upd]
print("%d %d" % (ans[0], ans[1]))
C. Corrupted Sort
ORZ徐神,鸡尾酒排序秒了
考虑如果我们已经得到了一个有序的数组,对其随机交换两项后,怎样在\(2n\)步之内还原数组
做法是先从前往后冒泡一遍(把较大的数归位),再从后往前冒泡一遍(把较小的数归位),此时操作数量\(2(n-1)\)恰好不会引发交换
实现的时候由于上限比较宽松可以直接重复上述操作
#include <bits/stdc++.h>
int main() {
std::ios::sync_with_stdio(false);
int n; std::cin >> n;
std::string s("");
#define IF_WIN std::cin >> s; if(s == "WIN") exit(0)
for(;;) {
for(int i = 1; i < n; ++i) {
std::cout << i << ' ' << i + 1 << std::endl;
IF_WIN;
}
for(int i = n; i > 1; --i) {
std::cout << i - 1 << ' ' << i << std::endl;
IF_WIN;
}
}
return 0;
}
D. Display
题目转化一下就是给出若干个\(h\)维向量,求个数最少的向量使得它们的和中的某一维超过\(s\)
枚举最后超出的是哪一维,很显然只选择这一维最大的那个向量是最优的
#include<cstdio>
#include<iostream>
#include<string>
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
int n,w,h,s,mx[N],ans_len=1e9; char ch[N],ans_ch;
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
RI i,j,k; for (cin>>n>>w>>h>>s,i=1;i<=n;++i)
{
string cur; cin>>cur;
for (j=1;j<=h;++j)
{
string row; cin>>row;
row="."+row+"."; int cnt=0;
for (k=1;k<row.size();++k)
if (row[k]!=row[k-1]) ++cnt;
if (cnt>mx[j]) mx[j]=cnt,ch[j]=cur[0];
}
}
for (i=1;i<=h;++i)
{
if (mx[i]==0) continue;
int tmp=(s+mx[i]-1)/mx[i];
if (tmp<ans_len) ans_len=tmp,ans_ch=ch[i];
}
for (i=1;i<=ans_len;++i) cout<<ans_ch;
return 0;
}
E. Easy Compare-and-Set
首先发现可以先处理\(w_i=1\)的操作,\(w_i=0\)的操作最后讨论一下即可
离散化后把数值看作点,一个\(w_i=1\)操作看作一条有向边,则题目等价于找一个从\(c\)出发的路径经过所有边恰好一次
注意到这等价于图存在欧拉回路/存在以\(c\)为起点的欧拉通路
有向图存在欧拉回路的条件:所有点出度入度相同;有向图存在欧拉通路的条件:有且仅有一个点出度比入度大\(1\)(起点),有且仅有一个点入度比出度大\(1\)(终点)
另外还需要判断图是否连通,具体实现看代码
#include<cstdio>
#include<iostream>
#include<vector>
#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=200005;
int n,c,a[N],b[N],w[N],in[N],out[N],fa[N],vis[N];
vector <pi> v[N]; vector <int> path;
inline int getfa(CI x)
{
return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
inline void addedge(CI x,CI y,CI z)
{
v[x].push_back(pi(y,z)); ++out[x]; ++in[y];
}
inline void DFS(CI now)
{
while (!v[now].empty())
{
auto [to,id]=v[now].back(); v[now].pop_back();
if (vis[id]) continue; DFS(to); vis[id]=1; path.push_back(id);
}
}
int main()
{
RI i; scanf("%d%d",&n,&c); vector <int> rst;
for (rst.push_back(c),i=1;i<=n;++i)
scanf("%d%d%d",&a[i],&b[i],&w[i]),rst.push_back(a[i]),rst.push_back(b[i]);
sort(rst.begin(),rst.end()); rst.erase(unique(rst.begin(),rst.end()),rst.end());
auto find=[&](CI x)
{
return lower_bound(rst.begin(),rst.end(),x)-rst.begin()+1;
};
for (c=find(c),i=1;i<=n;++i) a[i]=find(a[i]),b[i]=find(b[i]);
int m=rst.size(); for (i=1;i<=m;++i) fa[i]=i;
vector <int> key; for (i=1;i<=n;++i)
if (w[i]==1) addedge(a[i],b[i],i),fa[getfa(a[i])]=getfa(b[i]),key.push_back(a[i]),key.push_back(b[i]);
for (i=1;i<key.size();++i) if (getfa(key[0])!=getfa(key[i])) return puts("No"),0;
int cnt_s=0,cnt_t=0,s,t;
for (i=1;i<=m;++i)
{
if (abs(in[i]-out[i])>1) return puts("No"),0;
if (in[i]+1==out[i]) ++cnt_s,s=i;
if (out[i]+1==in[i]) ++cnt_t,t=i;
}
if ((cnt_s==0&&cnt_t==0)||(cnt_s==1&&cnt_t==1&&s==c))
{
DFS(c); reverse(path.begin(),path.end());
for (i=1;i<=n;++i) if (w[i]==1&&!vis[i]) return puts("No"),0;
int ots=0; for (i=1;i<=n;++i) if (w[i]==1&&b[i]!=c) ots=i;
vector <int> ans; for (i=1;i<=n;++i) if (w[i]==0&&a[i]!=c) ans.push_back(i),vis[i]=1;
for (auto cur:path)
{
ans.push_back(cur);
if (cur==ots)
{
for (i=1;i<=n;++i) if (!vis[i]) ans.push_back(i),vis[i]=1;
}
}
for (i=1;i<=n;++i) if (!vis[i]) return puts("No"),0;
puts("Yes"); for (auto x:ans) printf("%d ",x);
} else puts("No");
return 0;
}
F. Futures Market Trends
数据范围不大,可以直接确定左端点后扩展右端点,考虑每次快速维护\(A,D\)的变化
比赛的时候徐神写了分别维护二次项的和、一次项的和的做法,后面发现其实直接用方差等于平方的期望减去期望的平方来做更简单
#include <bits/stdc++.h>
using llsi = long long signed int;
llsi d, c[3003], x[3003];
long double P;
int main() {
std::ios::sync_with_stdio(false);
std::cin >> d >> P;
for(int i = 1; i <= d; ++i) std::cin >> c[i]; c[0] = 0;
for(int i = 1; i <= d; ++i) x[i] = c[i] - c[i - 1];
int pos = 0, neg = 0;
for(int i = 1; i <= d; ++i) {
llsi sx = 0, sx2 = 0;
for(int j = i + 1; j <= d; ++j) {
sx2 += x[j] * x[j];
sx += x[j];
int n = j - i;
if(n >= 2 && (long double)sx * sx >= P * P * (long double)(n * sx2 - sx * sx)) {
if(sx > 0) pos += 1;
if(sx < 0) neg += 1;
}
}
}
std::cout << pos << ' ' << neg << std::endl;
return 0;
}
G. Grammar Path
题目看着都是如此的毒瘤
H. Heroes of Coin Flipping
祁神想了挺久的这个题,搞了个虚树+大讨论的做法,后面也没写过不知道正确性(主要题解也看不懂是俄语的)
I. Integer Square
签到题,直接枚举即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int s;
int main()
{
scanf("%d",&s);
for (RI i=0;i*i<=s;++i) for (RI j=0;j*j<=s;++j)
if (i*i+j*j==s)
{
printf("%d %d\n",0,0);
printf("%d %d\n",i,j);
printf("%d %d\n",-j,i);
printf("%d %d\n",i-j,i+j);
return 0;
}
return puts("Impossible"),0;
}
J. Joint Password Storage
题目都哈人的一笔,鉴定为不可做题
K. Keys and Locks Boolean Logic
数电题目来了,祁神后面提出一种通用做法,先找出最简的或与式,然后手玩一下可以找到怎么构造与逻辑和或逻辑
但感觉不知道能不能在限制内做出来,同时这题的输入输出感觉都要写大模拟,做不了一点
L. Lost Permutation
ORZ群论大师徐神大力秒了此题,不然我们队后面两个半小时就只能玩手机了
考虑令\(f=[1,2,3,\cdots,n]\),最后得到的结果一定也是\([1,2,3\cdots,n]\)
那么如果我们交换\(f\)中的两项会得到什么呢(假设交换了\(x,y\)),手玩一下会发现最后会得到一个只有两个数没有还原的排列
并且我们惊喜地发现这个序列中没有还原的两个数恰好是\(\pi(x),\pi(y)\)上的数
那么我们很容易想到一个naive的做法,例如固定\(x\),问出\((x,y_1),(x,y_2)\),找到它们返回的没有还原的数中的众数就是\(\pi(x)\)的值
但现在的问题是我们只能问两次,上面的做法看似完全不可行
不过徐神发现可以只问\([2,1,3,4,\cdots,n-1,n]\)(只交换前两个数)和\([1,3,4,5,\cdots,n,2]\)(固定第一个数,其余的数循环移位)这两个排列,用它们构造出所有的询问情况
(因为询问的信息可以相乘再利用,比如询问\(f_1,f_2\)得到的结果为\(g_1,g_2\),那么询问\(f_1\cdot f_2\)的结果就是\(g_1\cdot g_2\))
总复杂度\(O(n^2)\),实现起来需要一定技巧和细节
#include <bits/stdc++.h>
struct pm: public std::vector<int> {
inline pm(size_t n): vector(n) {}
friend pm operator*(const pm &a, const pm &b) {
pm c(a.size());
for(int i = 0; i < a.size(); ++i) c[i] = b[a[i]];
return c;
}
};
void work() {
int n; std::cin >> n;
std::cout << "? 2 1";
for(int i = 3; i <= n; ++i) std::cout << ' ' << i;
std::cout << std::endl;
pm a(n); for(auto &a: a) std::cin >> a, --a;
std::cout << "? 1";
for(int i = 3; i <= n; ++i) std::cout << ' ' << i;
std::cout << " 2" << std::endl;
pm b(n), bi(n), bs(n), be(n); for(auto &b: b) std::cin >> b, --b;
for(int i = 0; i < n; ++i) bi[b[i]] = i;
for(int i = 0; i < n; ++i) be[i] = bs[i] = i;
for(int i = 0; i < n - 1; ++i) be = b * be;
std::vector<std::vector<int>> sns(n, std::vector<int>());
for(int i = 1; i < n; ++i) {
int pi = i;
pm ars(n); for(int j = 0; j < n; ++j) ars[j] = j;
ars = be * a * bs * ars;
// std::cerr << "debug ars:";
// for(int j = 0; j < n; ++j) std::cerr << ars[j] + 1 << char(j == n - 1 ? 10 : 32);
for(int j = 0; j < n; ++j) if(ars[j] != j) {
sns[0].push_back(j);
sns[i].push_back(j);
}
bs = b * bs;
be = bi * be;
}
std::sort(sns[0].begin(), sns[0].end());
int a0, a0c = 0; {
int l = 0, r = 0;
while(l < sns[0].size()) {
while(r < sns[0].size() && sns[0][r] == sns[0][l]) r += 1;
if(r - l > a0c) {
a0 = sns[0][l];
a0c = r - l;
}
l = r;
}
}
std::cout << "! " << a0 + 1;
for(int i = 1; i < n; ++i) for(auto sns: sns[i])
if(sns != a0) std::cout << ' ' << sns + 1;
std::cout << std::endl;
return ;
}
int main(void) {
std::ios::sync_with_stdio(false);
int t; std::cin >> t;
while(t--) work();
return 0;
}
M. Mind the Gap
祁神开场写的,我题目都没看
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n, A[N];
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
A[0]=0;
for (int i=1; i<=n; ++i) cin >> A[i];
sort(A+1, A+n+1);
int L=1, R=(int)1e9;
int ans=0;
while (L<R){
int M = L+(R-L)/2;
bool f1=false, f2=false;
for (int i=0; i<n; ++i) if (A[i+1]-A[i]>M) f1=true;
for (int i=0; i<n-1; ++i) if (A[i+2]-A[i]<=M) f2=true;
if (f1 && f2) break;
else if (f1) L=M+1;
else if (f2) R=M;
else{ans=M; break;}
}
cout << ans << '\n';
return 0;
}
N. Nunchucks Shop
我只看了个题意就被祁神秒了
枚举在一边的珠子的个数,根据奇偶性讨论一下中心对称的方案数
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 55;
int C[N][N];
int n, k;
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
C[1][0]=C[1][1]=C[0][0]=1;
for (int i=2; i<N; ++i){
C[i][0]=C[i][i]=1;
for (int j=1; j<i; ++j){
C[i][j]=C[i-1][j-1]+C[i-1][j];
// printf("C(%lld %lld)=%lld\n", i, j, C[i][j]);
}
}
cin >> n >> k;
int ans=0;
for (int x=0; x<=n; ++x){
if (k-x<0 || k-x>n) continue;
int res=0;
if (n%2==0 && x%2==1) res += C[n][x]/2;
else res += (C[n][x]-C[n/2][x/2])/2+C[n/2][x/2];
if (k%2==0 && x==k/2) res*=2;
ans += res;
// printf("x=%lld res=%lld\n", x, res);
}
cout << ans << '\n';
return 0;
}
Postscript
这场题感觉没啥区分度,前面的题很简单后面的题又极难,而且感觉考的都比较诡异,和国内的风格不太像说是
标签:case,std,North,Northern,puts,Contest,int,ITMO,break From: https://www.cnblogs.com/cjjsb/p/17985718