CF1470 题解
CF1470A
考虑贪心一下,将 \(k\) 倒序排序,然后给大的 \(k\) 配尽可能小的礼物即可。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e6+9,mod=1e9+7;
inline long long read() {
long long res=0, w=1; char c=getchar();
while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
while(isdigit(c)) {res=res*10+c-48; c=getchar();}
return res*w;
}
int T,n,m,c[N],k[N];
signed main() {
T=read();
while(T--) {
n=read(), m=read();
rep(i,1,n) k[i]=read();
rep(i,1,m) c[i]=read();
sort(k+1,k+n+1); reverse(k+1,k+n+1);
int pos=1,ans=0;
rep(i,1,n) {
if(pos<=m&&pos<=k[i]) {
ans+=c[pos++];
} else {
ans+=c[k[i]];
}
}
printf("%lld\n",ans);
}
return 0;
}
CF1570B
这个条件相当于两个数相乘等于平方数,相当于两个数去除平方因子后相等。
我们定义一群相同的数为一个块。容易发现,在 \(2\) 秒后就不会发生块的“合并”。第一秒的时候,相等的数全部合并起来。第二秒的时候,大小为偶数的块和 \(1\) 合并起来。然后算一下即可。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e6+9,mod=1e9+7;
inline long long read() {
long long res=0, w=1; char c=getchar();
while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
while(isdigit(c)) {res=res*10+c-48; c=getchar();}
return res*w;
}
int t,n,q,a[N],pr[N],cnt;
bool vst[N];
void sieve(int n){
for(int i=2;i<=n;i++){
if(!vst[i]) pr[++cnt]=i;
for(int j=1;j<=cnt&&i*pr[j]<=n;j++){
vst[i*pr[j]]=1;
if(i%pr[j]==0) break;
}
}
}
signed main() {
t=read();
sieve(1000000);
while(t--) {
n=read();
rep(i,1,n) {
a[i]=read();
for(int j=1;j<=cnt&&pr[j]*pr[j]<=a[i];j++) {
while(a[i]%(pr[j]*pr[j])==0) a[i]/=(pr[j]*pr[j]);
}
}
sort(a+1,a+n+1); a[n+1]=0;
int mx=0,sum=0,cnt=0,cnt1=0;
rep(i,1,n+1) {
if(a[i]!=a[i-1]) {
mx=max(mx,cnt);
if(cnt%2==0) sum+=cnt;
if(a[i-1]==1&&cnt%2!=0) cnt1=cnt;
cnt=1;
} else cnt++;
}
q=read();
while(q--) {
int w=read();
if(w==0) printf("%lld\n",mx);
else printf("%lld\n",max(mx,sum+cnt1));
}
}
return 0;
}
CF1570C
一些很妙的观察。我们发现实际上,\(p+1,p+2,...,n,1,...,p-1\) 所持有的数量是单调不升的,所以我们可以在确定了一个 \(p+x\) 后通过二分来找到。又有一个发现,对于前 \(n/2\) 论,每轮都会使得拥有 \(> k\) 张牌的人多至少一个,意味着在 \(\sqrt n\) 轮后,必有一段 \(\sqrt n\) 的连续区间使得其都 \(>k\)。我们把序列分块,每块中挑一个数来问,必然能问到一个 \(>k\) 的,然后二分即可。为了避免麻烦,\(n<...\) 的时候设块长为 1 暴力即可。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;
inline long long read() {
long long res=0, w=1; char c=getchar();
while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
while(isdigit(c)) {res=res*10+c-48; c=getchar();}
return res*w;
}
int n,k,bs;
int qry(int x) {
printf("? %d\n",(x-1)%n+1); fflush(stdout);
return read();
}
int main() {
n=read(), k=read();
bs=(n>900?400:1);
rep(i,1,bs) qry(1);
int l=0,r=0;
for(int i=1;i<=n;i+=bs) {
int p=qry(i); if(p==k) continue;
if(p>k) r=i; else l=i;
} r+=n*(l>r);
while(l<=r) {
int mid=(l+r)/2, p=qry(mid);
if(p==k) {
printf("! %d",(mid-1)%n+1);
return 0;
}
if(p>k) r=mid-1;
else l=mid+1;
}
throw "shit";
return 0;
}
CF1470D
不连通无解。然后考虑证明其他情况都有解。一个非常有意思的证明,就是归纳证明,在证明的同时也能搞出解法。
考虑 \(dfs\) 序。假设目前 \(n-1\) 的图有解,考虑加入一个非割点(可以按照 \(dfs\) 序加入来保证),如果它已经和选择的点有连边,那么自然可行;否则我们在这个点上放老师即可。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=3e5+9;
inline long long read() {
long long res=0, w=1; char c=getchar();
while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
while(isdigit(c)) {res=res*10+c-48; c=getchar();}
return res*w;
}
int T,n,m,pos[N],dfn[N],tick,tag[N];
vector<int>e[N];
void dfs(int u) {
dfn[u]=++tick,pos[tick]=u;
for(auto v:e[u]) if(!dfn[v]) dfs(v);
}
int main() {
T=read();
while(T--) {
n=read(), m=read();
rep(i,1,n) tag[i]=dfn[i]=0, e[i].clear();
tick=0;
rep(i,1,m) {
int u=read(), v=read();
e[u].push_back(v), e[v].push_back(u);
}
dfs(1);
bool flag=0;
rep(i,1,n) if(!dfn[i]) {flag=1; break;}
if(flag) {puts("NO"); continue;}
puts("YES");
vector<int>ans;
rep(i,1,n) {
int u=pos[i];
tag[u]=1;
for(auto v:e[u]) tag[u]&=!tag[v];
if(tag[u]) ans.push_back(u);
}
printf("%d\n",(int)ans.size());
for(auto x:ans) printf("%d ",x);
puts("");
}
return 0;
}
CF1470E
非常的妙啊。首先一种翻转唯一对应一种最终结果的数列,所以最终方案数 \(p(n,c)=\sum_{i=0}^{c}\binom{n-1}{i}\)。
考虑设一个 \(L(i,c)\) 表示考虑 \([i,c]\) 后缀用花费不超过 \(c\) 的代价得到的最终序列按字典序排序时,所作的第一次翻转操作,的序列。序列中每个元素可以写成 \(l,r,p\) 的形式,代表:翻转区间左端点,翻转区间右端,这种东西对应的情况数。
设 \(F(i,c,k)\) 表示 \(L(i,c)\) 的第 \(k\) 大的方案。这个可以通过二分解决,即二分 \(pos\) 使得 \(w_1+...+w_{pos}\ge k\)。
\(L\) 的求法很秒。考虑从 \(L(i+1)\) 转移到 \(L(i)\)。首先 \(i\) 有 \(c\) 种翻法。每个 \(L\) 维护一个双端队列,考虑翻 \([i,i+j]\) 时的情况,发现要么被放到队列左边,要么放到右边,取决于 \(a_{i+j}\) 与 \(a_i\) 相比的大小。
然后问询最多调用 \(c\) 次 \(F\),也就是说复杂度 \(O(nc^2+qc\log(nc))\)。
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=3e4+9;
typedef pair<int,int>pii;
inline long long read() {
long long res=0, w=1; char c=getchar();
while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
while(isdigit(c)) {res=res*10+c-48; c=getchar();}
return res*w;
}
int T,n,c,q,a[N];
int C(int x,int y,int ret=1) {
if(x<=1) return !y;
if(x-1<y) return 0;
per(i,x-1,x-y) ret*=i;
rep(i,1,y) ret/=i;
return ret;
}
int calc(int len,int c,int ret=0) {rep(i,0,c) ret=(ret+C(len,i)); return ret;}
int ql[9],qr[9],s[6][N],sw[6][N*10];
struct node {
int l,r,w;
node(int _l=0,int _r=0,int _w=0) {l=_l,r=_r,w=_w;}
} dq[6][N*10];
bool cmp(const node &x,const node &y) {return a[x.r]<a[y.r];}
node F(int p,int c,int k) {
int lst=s[c][p];
int l=lst+1,r=qr[c]-ql[c]+2;
while(l<r) {
int mid=(l+r)/2;
if(sw[c][mid]-sw[c][lst]>=k) r=mid;
else l=mid+1;
}
return node(dq[c][ql[c]+l-1].l,dq[c][ql[c]+l-1].r,sw[c][l-1]-sw[c][lst]);
}
signed main() {
T=read();
while(T--) {
n=read(), c=read(), q=read();
int lim=calc(n,c);
rep(i,1,n) a[i]=read();
rep(k,1,c) {
ql[k]=12002,qr[k]=12001; rep(i,1,n) s[k][i]=0;
dq[k][++qr[k]]=node(n,n,1);
per(i,n-1,1) {
int dl=0,dr=0;
rep(j,1,min(k,n-i)) {
if(a[i+j]<a[i]) dq[k][ql[k]-(++dl)]=node(i,i+j,calc(n-i-j,k-j)), s[k][i+1]++;
else dq[k][qr[k]+(++dr)]=node(i,i+j,calc(n-i-j,k-j));
}
if(dl) sort(dq[k]+ql[k]-dl,dq[k]+ql[k],cmp), ql[k]-=dl;
if(dr) sort(dq[k]+qr[k]+1,dq[k]+qr[k]+dr+1,cmp), qr[k]+=dr;
}
rep(i,1,n) s[k][i]=s[k][i-1]+s[k][i];
rep(i,ql[k],qr[k]) sw[k][i-ql[k]+1]=sw[k][i-ql[k]]+dq[k][i].w;
}
while(q--) {
int pos=read(), k=read();
if(k>lim) {puts("-1"); continue;}
vector<pii>v;
int p=1,rem=c,ok=k;
while(rem&&p<=n) {
node tmp=F(p,rem,k);
v.push_back(pii(tmp.l,tmp.r));
k-=tmp.w, rem-=(tmp.r-tmp.l), p=tmp.r+1;
}
bool flag=1;
for(auto pp:v) {
if(pp.fi<=pos&&pp.se>=pos) {
flag=0;
printf("%lld\n",a[pp.se+pp.fi-pos]);
break;
}
} if(flag) printf("%lld\n",a[pos]);
}
}
return 0;
}