XIX Russia Team Open, High School Programming Contest 解题报告
A
温暖的签到题。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
const int N=2e5+7;
ll a[N],mx[N],m[N];
int main(){
int n=input();
ll mxx=0;
for(int i=1;i<=n;i++){
m[i]=input();
for(int j=1;j<=m[i];j++){
ll x=input();
mx[i]=max(mx[i],x);
}
mxx=max(mx[i],mxx);
}
ll Ans=0;
for(int i=1;i<=n;i++){
Ans+=(mxx-mx[i])*m[i];
}
printf("%lld\n",Ans);
}
M
尺取法,温暖的签到题。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
const int N=1e5+7;
int a[N];
int main(){
int n=input(),k=input();
for(int i=1;i<=n;i++){
a[i]=input();
}
int Ans=1;
for(int r=1,l=1;r<=n;r++){
if(r-l+1>1){
if(a[r]!=a[r-1]) Ans=max(r-l+1,Ans);
else l=r;
}
}
printf("%d\n",Ans);
}
D
构造,易知如果题目给了\(\frac{n(n-1)}{2}\)个以上的大小关系,那么a数组与b数组已经被确定,又因为a数组是一个排列,所以b数组必定无法满足条件。我们可以找到一个所给大小关系不包括的两个位置,强行让这两个位置满足条件,其它位置让a,b的对应值相等,即可满足题设条件。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
const int N=1e5+7;
int n,m;
map <int,int> mp[N];
int a[N],b[N];
int main(){
n=input(),m=input();
for(int i=1;i<=m;i++){
int l=input(),r=input();
if(l>r) swap(l,r);
mp[l][r]=1;
}
int flag=1,posi,posj;
for(int i=1;i<=n&&flag;i++){
for(int j=i+1;j<=n&&flag;j++){
if(!mp[i][j]){
posi=i,posj=j;
flag=0;
}
}
}
if(flag) printf("NO\n");
else{
printf("YES\n");
b[posi]=1,b[posj]=1;
a[posi]=1,a[posj]=2;
int cnt=2;
for(int i=1;i<=n;i++){
if(i==posi||i==posj) continue;
a[i]=b[i]=++cnt;
}
for(int i=1;i<=n;i++) printf("%d%c",a[i],i==n? '\n':' ');
for(int i=1;i<=n;i++) printf("%d%c",b[i],i==n? '\n':' ');
}
}
L
考虑单周,双周,所有时间对答案的影响,不妨设单周为\(odd\),双周为\(even\),所有时间能承载的人数为\(\frac{odd*a+even*b}{k}\),单周承载人数为\(\frac{odd*a}{k-even}\),双周承载人数为\(\frac{even*b}{k-odd}\),故答案最小值为这三个数中最小值。不过考虑答案的可行性,要保证分母为正整数,不满足条件的情况不需要考虑。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
ll t,n,a,b,k;
int main(){
t=input(),n=input(),a=input(),b=input(),k=input();
ll odd,even;
odd=n/2+n%2;
even=n/2;
if(n<k) printf("0\n"),exit(0);
ll Ans=(a*odd+b*even)/k;
if(k>even) Ans=min((a*odd)/(k-even),Ans);
if(k>odd) Ans=min((b*even)/(k-odd),Ans);
printf("%lld\n",min(Ans,t));
}
B
模拟题,看见\(LaTeX\)还感觉挺亲切的(之前帮老师用LaTeX排版论文,熬了通宵)。拿个map存一下顺序,直接在bibliography里匹配就好了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define PIS pair <int,string>
#define fr first
#define sc second
#define mp make_pair
string tex;
vector <PIS> bib;
map <string,int> bid;
void input(){
string s;
while(getline(cin,s)){
if(s=="\\begin{thebibliography}{99}") break;
tex+=s;
}
bib.clear();
int cnt=0;
while(getline(cin,s)){
if(s=="\\end{thebibliography}") break;
bib.pb(mp(++cnt,s));
}
}
string getname(int pos){
string res;
for(int i=pos;;i++){
if(tex[i]=='}') break;
res+=tex[i];
}
return res;
}
bool work(){
int id=0;
for(int i=0;i<tex.length();i++){
if(tex[i]=='\\'&&tex[i+1]=='c'&&tex[i+2]=='i'&&tex[i+3]=='t'&&tex[i+4]=='e'&&tex[i+5]=='{'){
bid[getname(i+6)]=++id;
}
}
// for(auto v:bid){
// cout<<v.fr<<" "<<v.sc<<endl;
// }
int f=1;
for(auto &v:bib){
string s=v.sc,name="";;
int id=v.fr,now=9;
while(s[now]!='}'){
name+=s[now++];
}
if(bid[name]!=id){
f=0;
v.fr=bid[name];
}
}
return f;
}
int main(){
input();
int flag=work();
if(flag) printf("Correct\n");
else{
printf("Incorrect\n");
sort(bib.begin(),bib.end());
printf("\\begin{thebibliography}{99}\n");
for(auto v:bib){
cout<<v.sc<<endl;
}
printf("\\end{thebibliography}\n");
}
}
I
第一次发觉unsigned int的编码的原理会导致unsigned int里存的数是对\(2^{32}\)取模的,这个地方wa了不少。其它没啥好说的,对于每一个数,找一个他前面比他小的数中最小的,找他后面比他大的数中最大的即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
const int N=2e7+7;
const ll mod=1ll<<32;
const ll inf=9223372036854775806;
ll a[N];
unsigned int b[N],x,y,z;
ll _min(ll a,ll b){
if(a<b) return a;
else return b;
}
ll _max(ll a,ll b){
if(a>b) return a;
else return b;
}
int main(){
int T=input();
while(T--){
ll n=input(),l=input(),r=input();x=input(),y=input(),z=input();
for(int i=0;i<=n+7;i++) a[i]=b[i]=0;
b[1]=input(),b[2]=input();
for(int i=3;i<=n;i++){
b[i]=(b[i-2]*x%mod+b[i-1]*y%mod+z)%mod;
}
for(int i=1;i<=n;i++){
a[i]=b[i]%(r-l+1)+l;
}
// for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl<<endl;
ll mi=inf,mx=-inf;
ll Ans=inf;
for(int i=1;i<=n;++i){
if(mi<a[i])Ans=_min(a[i]*mi,Ans);
mi=_min(a[i],mi);
}
for(int i=n;i>=1;--i){
if(mx>a[i]) Ans=_min(a[i]*mx,Ans);
mx=_max(a[i],mx);
}
if(Ans==inf) printf("IMPOSSIBLE\n");
else printf("%lld\n",Ans);
}
}
K
对于循环节我们需要知道出现过了哪些字符。对于串首,串首末尾的与循环节成分相同的字符我们可以去掉。要满足题目条件我们需要经过我们处理的串首要相等并且循环节成分相同我们便可以认为这两个串互为子序列。那么我们拿map映射一下就可以解决问题了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
#define PII pair <string,string>
#define fr first
#define sc second
#define mp make_pair
const int N=1e5+7;
map<PII,vector<int>> sp;
PII a[N];
int main(){
int n=input();
for(int i=1;i<=n;i++){
cin>>a[i].fr>>a[i].sc;
string t1="0";
string t2="00000000000000000000000000";
for(int j=0;j<a[i].sc.length();j++){
t2[a[i].sc[j]-'a']='1';
}
int j;
for(j=a[i].fr.size()-1;j>=0;j--){
if(t2[a[i].fr[j]-'a']=='0') break;
}
for(int k=0;k<=j;k++){
t1+=a[i].fr[k];
}
// cout<<t1<<" "<<t2<<endl;
sp[mp(t1,t2)].push_back(i);
}
printf("%d\n",sp.size());
for(auto v:sp){
printf("%d ",v.sc.size());
for(auto t:v.sc)
printf("%d ",t);
printf("\n");
}
}
E
枚举起点和终点进行搜索,如果碰到其它🐎则避开它走其它路径。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
#define PII pair <int,int>
#define PPP pair <PII,PII>
#define fr first
#define sc second
#define mp make_pair
int dx[]={1,1,-1,-1,2,2,-2,-2};
int dy[]={2,-2,2,-2,1,-1,1,-1};
int p[10][10],dep[10][10];
queue <PII> Q;
stack <PII> st;
vector<PPP> Ans;
char s[10];
bool check(int x,int y){
if(0<=x&&x<8&&0<=y&&y<8) return 1;
else return 0;
}
void bfs(PII s,PII t){
while(!Q.empty()) Q.pop();
memset(dep,-1,sizeof(dep));
Q.push(t);
dep[t.fr][t.sc]=0;
while(!Q.empty()&&dep[s.fr][s.sc]==-1){
int x=Q.front().fr,y=Q.front().sc;Q.pop();
for(int i=0;i<8;i++){
int tx=x+dx[i],ty=y+dy[i];
if(!check(tx,ty)||dep[tx][ty]!=-1) continue;
dep[tx][ty]=dep[x][y]+1;
Q.push(mp(tx,ty));
}
}
int x1=s.fr,y1=s.sc;
int x2=t.fr,y2=t.sc;
while(x1!=x2||y1!=y2){
while(!st.empty()) st.pop();
while(!p[x1][y1]){
if(st.size()==0) p[x1][y1]=1;
st.push(mp(x1,y1));
for(int i=0;i<8;i++){
int tx=x1+dx[i],ty=y1+dy[i];
if(check(tx,ty)&&dep[x1][y1]-dep[tx][ty]==1){
x1=tx,y1=ty;
break;
}
}
}
st.push(mp(x1,y1));
p[x1][y1]=0;
PII pre=st.top();st.pop();
while(!st.empty()) Ans.push_back(mp(pre,st.top())),pre=st.top(),st.pop();
}
}
int main(){
int n=input();
for(int i=1;i<=n;i++){
scanf("%s",s);
p[s[1]-'1'][s[0]-'a']=1;
}
for(int i=0;i<8&&n;i++) for(int j=0;j<8&&n;j++){
if(n){
n--;
int flag=1;
if(p[i][j]) continue;
for(int _i=7;_i>=0&&flag;_i--) for(int _j=7;_j>=0&&flag;_j--){
if(p[_i][_j]){
flag=0;
bfs(mp(i,j),mp(_i,_j));
}
}
}
}
printf("%d\n",Ans.size());
for(auto v:Ans){
printf("%c%d-%c%d\n",v.fr.sc+'a',v.fr.fr+1,v.sc.sc+'a',v.sc.fr+1);
}
}
C
对于本题我们可以将其抽象为两个问题:
- 对于若干个集合找到最大的集合和最小的集合,并判断这两个集合的大小差值是否小于等于1。
- 对于一个大集合找到一个不属于小集合的元素,并且将这个元素从大集合丢到小集合中去。
对于问题1,我们有多种解决方式例如线段树,平衡树等。在这里我们可以使用set来降低我们编程的复杂度。
对于问题2,我们可以尝试用线段树做差来判断某一子集是否相等,在线段树上二分找到答案。有点类似于可持久化线段树,因为解一定在大集合的一条链上,所以我们可以通过在线段树上二分来求得答案。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
#define PII pair <int,int>
#define fr first
#define sc second
#define mp make_pair
#define ls(x) t[x].l
#define rs(x) t[x].r
const int N=2e5+7;
set <PII> st;
struct node{
int sum,l,r;
}t[N*40];
int rt[N],cnt;
void update(int &rt,int l,int r,int pos,int val){
if(!rt) t[++cnt].sum+=val,rt=cnt;
else t[rt].sum+=val;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) update(ls(rt),l,mid,pos,val);
else update(rs(rt),mid+1,r,pos,val);
}
int query(int x,int y,int l,int r){
if(l==r) return l;
int mid=(l+r)>>1;
if((t[ls(x)].sum-t[ls(y)].sum)>=1) return query(ls(x),ls(y),l,mid);
else if((t[rs(x)].sum-t[rs(y)].sum)>=1) return query(rs(x),rs(y),mid+1,r);
}
set<PII>::iterator iter1;
set<PII>::iterator iter2;
bool check(){
if(st.size()<=1) return 0;
iter1=st.begin();iter2=st.end();--iter2;
if(((*iter2).fr-(*iter1).fr)>1) return 1;
else return 0;
}
struct as{
int from,to,kind;
};
vector <as> Ans;
int main(){
int n=input(),m=input();
for(int i=1;i<=n;i++){
int c=input();
for(int j=1;j<=c;j++){
int a=input();
update(rt[i],1,m,a,1);
}
st.insert(mp(t[rt[i]].sum,i));
}
while(check()){
int from,to,kind;
iter1=st.begin();iter2=st.end();--iter2;
from=(*iter2).sc,to=(*iter1).sc;
kind=query(rt[from],rt[to],1,m);
update(rt[from],1,m,kind,-1);
update(rt[to],1,m,kind,1);
PII ff=(*iter2),tt=(*iter1);
st.erase(iter1),st.erase(iter2);
ff.fr=t[rt[from]].sum,tt.fr=t[rt[to]].sum;
st.insert(ff),st.insert(tt);
Ans.push_back((as){from,to,kind});
}
printf("%d\n",Ans.size());
for(auto v:Ans){
printf("%d %d %d\n",v.from,v.to,v.kind);
}
}