Codeforces Round #569 (Div. 1)
A Valeriy and Deque
考虑先走n-1步,那么走完了n-1步后最大的数一定就在最前面了,接下来的操作会让后面的n-1个数进入循环,那么对于一个询问\(m_i\),如果\(m_i<n-1\),那么直接回答即可,否则将\(m_i\)对n-1取模,然后回答。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define vic vector<int>
#define vit vic::iterator
#define pir pair<int,int>
#define fr first
#define sc second
#define mp(x,y) make_pair(x,y)
#define rsort(x,y) sort(x,y),reverse(x,y)
using namespace std;
inline char gc() {
// static char buf[100000],*p1,*p2;
// return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
return getchar();
}
template<class T>
int read(T &ans) {
ans=0;char ch=gc();T f=1;
while(!isdigit(ch)) {
if(ch==EOF) return -1;
if(ch=='-') f=-1;
ch=gc();
}
while(isdigit(ch))
ans=ans*10+ch-'0',ch=gc();
ans*=f;return 1;
}
template<class T1,class T2>
int read(T1 &a,T2 &b) {
return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}
template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}
typedef long long ll;
const int Maxn=1100000;
const int inf=0x3f3f3f3f;
int n,t,a[Maxn],c[Maxn];
ll x;
pir b[Maxn];
signed main() {
// freopen("test.in","r",stdin);
// freopen("out","w",stdout);
read(n,t);
for(int i=1;i<=n;i++) read(a[i]);
deque<int> q;
for(int i=1;i<=n;i++) q.push_back(a[i]);
for(int i=1;i<n;i++) {
int x=q.front(); q.pop_front();
int y=q.front(); q.pop_front();
b[i]=mp(x,y);
if(x<y) swap(x,y);
q.push_front(x);
q.push_back(y);
}
int mx=q.front(); q.pop_front();
for(int i=1;i<n;i++) c[i]=q.front(),q.pop_front();
while(t--) {
read(x);
if(x<n) printf("%d %d\n",b[x].fr,b[x].sc);
else {
x-=n;
printf("%d %d\n",mx,c[x%(n-1)+1]);
}
}
return 0;
}
B Tolik and His Uncle
构造题。
\((1,1)\rightarrow(n,m)\rightarrow(1,2)\rightarrow(n,m-1)\rightarrow\cdots\rightarrow(n,1)\rightarrow(2,1)\rightarrow(n-1,m)\rightarrow\cdots\)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define vic vector<int>
#define vit vic::iterator
#define pir pair<int,int>
#define fr first
#define sc second
#define mp(x,y) make_pair(x,y)
#define rsort(x,y) sort(x,y),reverse(x,y)
using namespace std;
inline char gc() {
// static char buf[100000],*p1,*p2;
// return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
return getchar();
}
template<class T>
int read(T &ans) {
ans=0;char ch=gc();T f=1;
while(!isdigit(ch)) {
if(ch==EOF) return -1;
if(ch=='-') f=-1;
ch=gc();
}
while(isdigit(ch))
ans=ans*10+ch-'0',ch=gc();
ans*=f;return 1;
}
template<class T1,class T2>
int read(T1 &a,T2 &b) {
return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}
template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}
typedef long long ll;
//const int Maxn=;
const int inf=0x3f3f3f3f;
int n,m;
void work1(int x,int y) {
for(int l=1,r=m;l<=m;l++,r--) printf("%d %d\n%d %d\n",x,l,y,r);
}
void work2(int x) {
for(int l=1,r=m;l<=r;l++,r--) {
if(l!=r) printf("%d %d\n%d %d\n",x,l,x,r);
else printf("%d %d\n",x,l);
}
}
signed main() {
// freopen("test.in","r",stdin);
read(n,m);
for(int l=1,r=n;l<=r;l++,r--) {
if(l!=r) work1(l,r);
else work2(l);
}
return 0;
}
C Serge and Dining Room
考虑要求的实际上就是最大的x,满足价格大于x的菜的数目大于钱数大于x的人数。
那么这个问题就是单点修改,然后查询后缀和,这样很不好搞,于是变成前缀修改,单点查询,然后就可以在线段树上二分了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define vic vector<int>
#define vit vic::iterator
#define pir pair<int,int>
#define fr first
#define sc second
#define mp(x,y) make_pair(x,y)
#define rsort(x,y) sort(x,y),reverse(x,y)
using namespace std;
inline char gc() {
// static char buf[100000],*p1,*p2;
// return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
return getchar();
}
template<class T>
int read(T &ans) {
ans=0;char ch=gc();T f=1;
while(!isdigit(ch)) {
if(ch==EOF) return -1;
if(ch=='-') f=-1;
ch=gc();
}
while(isdigit(ch))
ans=ans*10+ch-'0',ch=gc();
ans*=f;return 1;
}
template<class T1,class T2>
int read(T1 &a,T2 &b) {
return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}
template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}
typedef long long ll;
const int Maxn=1100000;
const int inf=0x3f3f3f3f;
int tl[Maxn<<2],tr[Maxn<<2],tn[Maxn<<2],bj[Maxn<<2];
int n,m,a[Maxn],b[Maxn],q,opt,x,y;
#define lch (root<<1)
#define rch (root<<1|1)
void build(int root,int l,int r) {
tl[root]=l,tr[root]=r;
if(l==r) return ;
int mid=l+r>>1;
build(lch,l,mid);
build(rch,mid+1,r);
}
inline void update(int root) {
tn[root]=max(tn[lch],tn[rch]);
}
inline void abj(int root,int x) {
tn[root]+=x;
bj[root]+=x;
}
void pushdown(int root) {
if(bj[root]) {
abj(lch,bj[root]);
abj(rch,bj[root]);
bj[root]=0;
}
}
void chang(int root,int x,int y) {
int l=tl[root],r=tr[root],mid=l+r>>1;
if(l==r) {
tn[root]+=y;
return ;
}
pushdown(root);
if(x<=mid) {
chang(lch,x,y);
update(root);
return ;
}
abj(lch,y);
chang(rch,x,y);
update(root);
}
int work(int root) {
if(tl[root]==tr[root]) return tl[root];
pushdown(root);
return work(tn[rch]>=1?rch:lch);
}
signed main() {
// freopen("test.in","r",stdin);
read(n,m);
build(1,1,1000000);
for(int i=1;i<=n;i++) {
read(a[i]);
chang(1,a[i],1);
}
for(int i=1;i<=m;i++) {
read(b[i]);
chang(1,b[i],-1);
}
read(q);
for(int i=1;i<=q;i++) {
read(opt,x,y);
if(opt==1) {
chang(1,a[x],-1);
a[x]=y;
chang(1,a[x],1);
}
else {
chang(1,b[x],1);
b[x]=y;
chang(1,b[x],-1);
}
if(tn[1]>=1) printf("%d\n",work(1));
else puts("-1");
}
return 0;
}
D Fedor Runs for President
考虑加上一条边会增加多少条简单路径,那么把这条边连接的两个点之间的链拎出来,树的形态就变成了一条链上每个点上挂着一颗树,那么答案就等于\(n^2-\sum_isize[i]^2\)。显然选的两个点一定是两个叶子,因为如果不是两个叶子,那么再继续往下走一定会更优。
考虑枚举选的两个点的lca,设\(i\)为当前枚举的lca的一个儿子,\(f_i\)为\(i\)这个子树中叶子到\(i\)的链上的点除了链上的子树外的点的数目的平方的和的最小值。转移为\(f_{root}=\min_i \{f_i+(size[root]-size[i])^2\}\)。
现在来考虑统计答案,那么对于当前点的两个儿子\(i,j\),其答案除了\(f_i+f_j\)外还有当前点的大小之和,即\(f_i+f_j+(n-size[i]-size[j])^2\),展开一下有\(n^2+(f_i+size[i]^2-2\cdot n\cdot size[i])+(f_j+size[j]^2-2\cdot n\cdot size[j])+2\cdot size[i]\cdot size[j]\),那么按照\(size\)排序后用单调栈处理即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define vic vector<int>
#define vit vic::iterator
#define pir pair<int,int>
#define fr first
#define sc second
#define mp(x,y) make_pair(x,y)
#define rsort(x,y) sort(x,y),reverse(x,y)
using namespace std;
inline char gc() {
// static char buf[100000],*p1,*p2;
// return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
return getchar();
}
template<class T>
int read(T &ans) {
ans=0;char ch=gc();T f=1;
while(!isdigit(ch)) {
if(ch==EOF) return -1;
if(ch=='-') f=-1;
ch=gc();
}
while(isdigit(ch))
ans=ans*10+ch-'0',ch=gc();
ans*=f;return 1;
}
template<class T1,class T2>
int read(T1 &a,T2 &b) {
return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}
template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}
typedef long long ll;
const int Maxn=1100000;
const int inf=0x3f3f3f3f;
int to[Maxn],nxt[Maxn],first[Maxn],tot=1;
int n,m,u,v,d[Maxn],siz[Maxn];
ll f[Maxn],res;
pair<ll,ll> q[Maxn],a[Maxn];
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
to[tot]=u;
nxt[tot]=first[v];
first[v]=tot++;
}
inline ll sqr(ll x) {
return x*x;
}
ll cal(pair<ll,ll> x,ll y) {
return y*x.fr+x.sc;
}
void work(int root,int fa) {
siz[root]=1,f[root]=1ll*n*n;
for(int i=first[root];i;i=nxt[i])
if(to[i]!=fa) work(to[i],root),siz[root]+=siz[to[i]];
for(int i=first[root];i;i=nxt[i]) if(to[i]!=fa) qmin(f[root],f[to[i]]+sqr(siz[root]-siz[to[i]]));
int tot=0;
for(int i=first[root];i;i=nxt[i]) if(to[i]!=fa) a[++tot]=mp(siz[to[i]],to[i]);
if(!tot) {
f[root]=1;
return ;
}
sort(a+1,a+tot+1);
int top=0; q[++top]=mp(2ll*siz[a[1].sc],f[a[1].sc]+sqr(siz[a[1].sc])-2ll*n*siz[a[1].sc]);
for(int i=2;i<=tot;i++) {
while(top&&cal(q[top-1],a[i].fr)<cal(q[top],a[i].fr)) top--;
qmin(res,cal(q[top],a[i].fr)+f[a[i].sc]+sqr(siz[a[i].sc])-2ll*n*siz[a[i].sc]+1ll*n*n);
pair<ll,ll> temp=mp(2ll*siz[a[i].sc],f[a[i].sc]+sqr(siz[a[i].sc])-2ll*n*siz[a[i].sc]);
q[++top]=temp;
}
}
signed main() {
// freopen("test.in","r",stdin);
read(n);
if(n==2) {
puts("2");
return 0;
}
for(int i=1;i<n;i++) {
read(u,v);
d[u]++,d[v]++;
add(u,v);
}
int rt;
for(int i=1;i<=n;i++) if(d[i]>1) {
rt=i;
break;
}
res=1ll*n*n;
work(rt,rt);
ll ans=1ll*n*(n-1);
ans+=1ll*n*n-res;
printf("%I64d\n",ans/2);
return 0;
}
E Alesya and Discrete Math
看到了题目,满脸的不可做,(于是我们看一下题解。所以我就把题解翻译一下就好了。
考虑朴素做法,不失一般性,我们设n为偶数,那么显然我们可以用二分找到使得\(f_i(x_i)=\frac{L}{2}\)的\(x_i\),那么按照\(x_i\)从大到小排序并重新编号,可以发现这个一定是对的。同时这个问题也被分成了两个规模为\(\frac{n}{2}\)的子问题,那么复杂度\(T(n)=2T(\frac{n}{2})+O(n\log w)\ ,\ T(n)=O(n\log n\log w)\),这个是过不了的,所以考虑优化这个过程。
我们要把它分成两个子问题,所以可以随机一个选一个函数\(f_i\),二分出上面的\(x_i\),那么把\(f_j(x_i)>\frac{L}{2}\)的函数放在左边,反之放在右边,如果等于的话就先单独记出来,能填到左边就填到左边,直到左边放到\(\frac{n}{2}\)个数,然后剩下的丢到右边。这时如果左边恰有\(\frac{n}{2}\)个数,那么完成,否则如果少于,就向右边找,否则向左边找。这个复杂度是\(O(n\log n+n\log w)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define vic vector<int>
#define vit vic::iterator
#define pir pair<int,int>
#define fr first
#define sc second
#define mp(x,y) make_pair(x,y)
#define rsort(x,y) sort(x,y),reverse(x,y)
using namespace std;
inline char gc() {
// static char buf[100000],*p1,*p2;
// return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
return getchar();
}
template<class T>
int read(T &ans) {
ans=0;char ch=gc();T f=1;
while(!isdigit(ch)) {
if(ch==EOF) return -1;
if(ch=='-') f=-1;
ch=gc();
}
while(isdigit(ch))
ans=ans*10+ch-'0',ch=gc();
ans*=f;return 1;
}
template<class T1,class T2>
int read(T1 &a,T2 &b) {
return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}
template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}
typedef long long ll;
const int Maxn=1100;
const int inf=0x3f3f3f3f;
int a[Maxn],b[Maxn],c[Maxn],tims;
ll n,L,v;
pair<ll,ll> ans[Maxn];
ll query(ll a,ll b) {
printf("? %I64d %I64d\n",a,b);
fflush(stdout); ll ans;
read(ans);
// ans=b;
// tims++;
return ans;
}
ll find(int x,ll k,ll l,ll r) {
ll mid=l+r>>1;
while(1) {
ll y=query(x,mid);
if(y==k) return mid;
if(y<k) l=mid+1;
else r=mid-1;
mid=l+r>>1;
}
}
void kth(int l,int r,int k,ll ql,ll qr) {
if(l==r) return ;
int x=rand()%(r-l+1)+l;
ll y=find(a[x],(L/n)*k,ql,qr);
int mid=k-l+1;
int l1=l-1,l2=r+1,l3=0;
c[++l3]=a[x];
for(int i=l;i<=r;i++) {
if(i==x) continue;
ll sxz=query(a[i],y);
if(sxz<(L/n)*k) b[--l2]=a[i];
if(sxz==(L/n)*k) c[++l3]=a[i];
if(sxz>(L/n)*k) b[++l1]=a[i];
}
while(l1<k&&l3) b[++l1]=c[l3--];
if(l1==k) {
v=y;
while(l3) b[--l2]=c[l3--];
for(int i=l;i<=r;i++) a[i]=b[i];
return ;
}
if(l1<k) {
for(int i=l;i<=r;i++) a[i]=b[i];
kth(l1+1,r,k,ql,qr);
return ;
}
while(l3) b[--l2]=c[l3--];
for(int i=l;i<=r;i++) a[i]=b[i];
kth(l,l1,k,ql,qr);
}
void solve(int l,int r,ll ql,ll qr) {
if(l==r) {
ans[a[l]]=mp(ql,qr);
return ;
}
int mid=l+r>>1;
kth(l,r,mid,ql,qr);
ll x=v;
solve(l,mid,ql,x);
solve(mid+1,r,x,qr);
}
signed main() {
// freopen("test.in","r",stdin);
// freopen("out","w",stdout);
read(n,L);
for(int i=1;i<=n;i++) a[i]=i;
solve(1,n,0,1000000000000000000ll);
puts("!");
for(int i=1;i<=n;i++) printf("%I64d %I64d\n",ans[i].fr,ans[i].sc);
fflush(stdout);
// printf("%d\n",tims);
return 0;
}