Educational Codeforces Round 110
比赛链接:https://codeforces.com/contest/1535
懒得一题一题写博客了。直接扔在一起吧(
A. Fair Playoff
题目链接:https://codeforces.com/contest/1535/problem/A
直接模拟。
#include <bits/stdc++.h>
#define pYES printf("YES\n");
#define pYes printf("Yes\n");
#define pyes printf("yes\n");
#define pNO printf("NO\n");
#define pNo printf("No\n");
#define pno printf("no\n");
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=200010;
int Q,a[5],b,c,d;
void work()
{
scanf("%d%d%d%d",&a[1],&a[2],&a[3],&a[4]);
int e=max(a[1],a[2]),f=max(a[3],a[4]);
if (e<f) swap(e,f);
sort(a+1,a+5);
if (e==a[4] && f==a[3]) pYES
else pNO
}
int main()
{
// freopen("data.txt","r",stdin);
scanf("%d",&Q);
while (Q--) work();
return 0;
}
B. Array Reodering
题目链接:https://codeforces.com/contest/1535/problem/B
考虑两个元素 \(x,y\):
- 如果 \(\gcd(x,y)>1\),那么无论顺序都有 \(1\) 的贡献。
- 如果 \(\gcd(x,y)=1\),且 \(x,y\) 有一个是偶数,让偶数排在前面就会有 \(1\) 的贡献。
- 如果 \(\gcd(x,y)=1\),且 \(x,y\) 均为奇数,无论顺序都没有贡献。
枚举一下即可。
#include <bits/stdc++.h>
#define pYES printf("YES\n");
#define pYes printf("Yes\n");
#define pyes printf("yes\n");
#define pNO printf("NO\n");
#define pNo printf("No\n");
#define pno printf("no\n");
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=200010;
int Q,n,m,a[N],b[N];
void work()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
int ans=0;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
{
if (__gcd(a[i],a[j])!=1) ans++;
else if (!(a[i]&1) || !(a[j]&1)) ans++;
}
cout<<ans<<"\n";
}
int main()
{
// freopen("data.txt","r",stdin);
scanf("%d",&Q);
while (Q--) work();
return 0;
}
C. Unstable String
题目链接:https://codeforces.com/contest/1535/problem/C
考虑每一个极长合法区间 \([l,r]\),它的贡献就是 \(\frac{(r-l+1)(r-l+2)}{2}\)。
双指针找到所有区间后再减一下相邻区间中 ?
的贡献即可。
#include <bits/stdc++.h>
#define pYES printf("YES\n");
#define pYes printf("Yes\n");
#define pyes printf("yes\n");
#define pNO printf("NO\n");
#define pNo printf("No\n");
#define pno printf("no\n");
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=200010;
int Q,n,m,pos[N];
char s[N];
void work()
{
ll ans=0;
scanf("%s",s+1);
n=strlen(s+1); m=0;
for (int i=1;i<=n;i++)
if (s[i]!='?') pos[++m]=i;
if (!m) { cout<<1LL*n*(n+1)/2LL<<"\n"; return; }
pos[m+1]=n+1;
for (int i=1,j;i<=m;i=j+1)
{
j=i;
while (j<m && ((pos[j+1]-pos[i])&1)==(s[pos[i]]!=s[pos[j+1]])) j++;
int c=(pos[j+1]-1)-(pos[i-1]+1)+1;
ans+=1LL*c*(c+1)/2LL;
if (i>1)
{
c=pos[i]-pos[i-1]-1;
ans-=1LL*c*(c+1)/2LL;
}
}
cout<<ans<<"\n";
}
int main()
{
// freopen("data.txt","r",stdin);
scanf("%d",&Q);
while (Q--) work();
return 0;
}
D. Playoff Tournament
题目链接:https://codeforces.com/contest/1535/problem/D
下文 \(n\) 即为题目中的 \(k\)。
先把题目中编号为 \(x\) 的比赛编号改为 \(2^n-x\)。
记 \(f[i]\) 表示比赛 \(i\) 为根的子树内,能赢到 \(i\) 的队伍数量。不难发现
注意叶子节点需要初始化。
每次修改只会修改一个点到根的路径的 \(f\),由于树高是 \(O(n)\) 的,所以每次修改都可以 \(O(n)\) 解决。
时间复杂度 \(O(Qn)\)。
#include <bits/stdc++.h>
#define pYES printf("YES\n");
#define pYes printf("Yes\n");
#define pyes printf("yes\n");
#define pNO printf("NO\n");
#define pNo printf("No\n");
#define pno printf("no\n");
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=300010;
int Q,n,m,f[N];
char s[N];
int main()
{
// freopen("data.txt","r",stdin);
scanf("%d",&n); n=(1<<n)-1;
scanf("%s",s+1);
for (int i=n;i>=1;i--)
{
if (i*2>n) { f[i]=1+(s[n-i+1]=='?'); continue; }
if (s[n-i+1]=='?') f[i]=f[i*2]+f[i*2+1];
if (s[n-i+1]=='0') f[i]=f[i*2+1];
if (s[n-i+1]=='1') f[i]=f[i*2];
}
scanf("%d",&Q);
while (Q--)
{
int x; char c[3];
scanf("%d%s",&x,c);
s[x]=c[0]; x=n-x+1;
for (int i=x;i;i>>=1)
{
if (i*2>n) { f[i]=1+(s[n-i+1]=='?'); continue; }
if (s[n-i+1]=='?') f[i]=f[i*2]+f[i*2+1];
if (s[n-i+1]=='0') f[i]=f[i*2+1];
if (s[n-i+1]=='1') f[i]=f[i*2];
}
cout<<f[1]<<"\n";
}
return 0;
}
E. Gold Transfer
题目链接:https://codeforces.com/contest/1535/problem/E
由于强制在线加点询问,所以几乎不用考虑什么数据结构维护了(定期重构除外)。
显然一次询问中是要不断取给定点到根中深度最小的且剩余物品的点购买。但是如果我们从根节点出发,并不好得知到给定点的路径。所以考虑从给定点出发找第一个能选的点。
那么显然直接倍增就可以了。由于一个点只会被删除一次,所以倍增次数是 \(O(Q)\) 的。
#include <bits/stdc++.h>
#define pYES printf("YES\n");
#define pYes printf("Yes\n");
#define pyes printf("yes\n");
#define pNO printf("NO\n");
#define pNo printf("No\n");
#define pno printf("no\n");
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=300010,LG=20;
int Q,n,m,rt=N-1,a[N],b[N],f[N][LG+1];
int find(int x)
{
for (int i=LG;i>=0;i--)
if (a[f[x][i]]) x=f[x][i];
return x;
}
int main()
{
// freopen("data.txt","r",stdin);
scanf("%d%d%d",&Q,&a[N-2],&b[N-2]);
for (int i=1;i<=Q;i++)
{
int opt;
scanf("%d",&opt);
if (opt==1)
{
scanf("%d%d%d",&f[i][0],&a[i],&b[i]);
if (!f[i][0]) f[i][0]=N-2;
for (int j=1;j<=LG;j++)
f[i][j]=f[f[i][j-1]][j-1];
}
else
{
int w,x; ll ans=0,cnt=0;
scanf("%d%d",&x,&w);
if (!x) x=N-2;
while (w)
{
if (!a[x]) break;
int p=find(x);
if (w>=a[p])
ans+=1LL*a[p]*b[p],cnt+=a[p],w-=a[p],a[p]=0;
else
ans+=1LL*w*b[p],cnt+=w,a[p]-=w,w=0;
}
cout<<cnt<<' '<<ans<<"\n";
fflush(stdout);
}
}
return 0;
}
F. String Distance
题目链接:https://codeforces.com/contest/1535/problem/F
根号分治(听说应该叫平衡规划?)。感觉不是正解。
记 \(n\) 为字符串数量,\(m\) 为字符串长度。
设定一个阈值 \(Lim\)。
- 对于 \(n\leq Lim\) 的点,我们可以直接枚举两个字符串,找到他们 LCP 和 LCS,然后中间的一部分可以暴力判断能否一次搞定。时间复杂度 \(O(n^2m)\)。
- 对于 \(n>Lim\) 的点,我们把所有字符串排序,使得所有字符的出现次数都相等的字符串排在一起。然后对于一个所有字符出现次数都相等的区间 \([l,r]\),先把它和区间外的贡献求出来,然后枚举区间内的所有字符串,再枚举一个进行操作的区间 \([i,j]\),暴力把这个字符串的区间 \([i,j]\) 排序后二分一下是否有相等的字符串即可。时间复杂度 \(O(nm^3\log m)\)。
取 \(Lim=4000\) 就可以过了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,Lim=4000;
int n,len,cnt[30];
char t[N];
vector<char> s[N];
bool check(int x,int y,int l=1,int r=len)
{
memset(cnt,0,sizeof(cnt));
for (int k=l;k<=r;k++)
cnt[s[x][k]-'a']++,cnt[s[y][k]-'a']--;
for (int i=0;i<26;i++)
if (cnt[i]) return 0;
return 1;
}
namespace S1
{
bool check1(int x,int l,int r)
{
for (int i=l+1;i<=r;i++)
if (s[x][i]<s[x][i-1]) return 0;
return 1;
}
void main()
{
ll ans=0;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (check(i,j))
{
int k=1,l=len;
while (s[i][k]==s[j][k]) k++;
while (s[i][l]==s[j][l]) l--;
if (check(i,j,k,l) && (check1(i,k,l) || check1(j,k,l))) ans++;
else ans+=2;
}
else ans+=1337;
cout<<ans;
}
}
namespace S2
{
int c1[30],c2[30];
vector<char> t;
bool cmp(vector<char> x,vector<char> y)
{
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for (int i=1;i<=len;i++)
c1[x[i]-'a']++,c2[y[i]-'a']++;
for (int i=0;i<26;i++)
if (c1[i]!=c2[i]) return c1[i]<c2[i];
for (int i=1;i<=len;i++)
if (x[i]!=y[i]) return x[i]<y[i];
return 19260817;
}
bool find(int l,int r)
{
while (l<=r)
{
int mid=(l+r)>>1;
bool flag=1;
for (int i=1;i<=len;i++)
{
if (s[mid][i]<t[i]) { flag=0; l=mid+1; break; }
if (s[mid][i]>t[i]) { flag=0; r=mid-1; break; }
}
if (flag) return 1;
}
return 0;
}
void main()
{
ll ans=0,ans1=0;
sort(s+1,s+1+n,cmp);
for (int l=1,r=1;l<=n;l=r=r+1)
{
while (r<n && check(l,r+1)) r++;
ans1+=1337LL*(r-l+1)*(n-(r-l+1));
ll res=0;
for (int i=1;i<=len;i++)
for (int j=i+1;j<=len;j++)
for (int k=l;k<=r;k++)
{
t=s[k];
sort(t.begin()+i,t.begin()+j+1);
if (t[i]!=s[k][i] && t[j]!=s[k][j] && find(l,r)) res++;
}
ans+=1LL*(r-l+1)*(r-l)-res;
}
cout<<ans+ans1/2LL;
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",t+1);
if (i==1) len=strlen(t+1);
for (int j=0;j<=len;j++)
s[i].push_back(t[j]);
}
if (n<=Lim) S1::main();
else S2::main();
return 0;
}