A k-Amazing Numbers
水题
#include<bits/stdc++.h>
#define io_opt ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define ll long long
using namespace std;
const int maxn=3e5+5;
int a[maxn],Start[maxn],l[maxn],ans[maxn],ANS[maxn];
int main()
{
io_opt;
int T,n;
cin>>T;
for(int id=1;id<=T;id++){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];ANS[i]=0;
if(Start[a[i]]!=id){
ans[a[i]]=i;
l[a[i]]=i;
Start[a[i]]=id;
}
else{
ans[a[i]]=max(ans[a[i]],i-l[a[i]]);
l[a[i]]=i;
}
}
for(int i=1;i<=n;i++){
if(Start[i]==id){
ans[i]=max(ans[i],n+1-l[i]);
if(ANS[ans[i]]==0){
ANS[ans[i]]=i;
}
}
}
int M=0x3f3f3f3f,S=0;
for(int i=1;i<=n;i++){
if(ANS[i]==0){
if(S==0) cout<<"-1 ";
else{
cout<<M<<' ';
}
}
else{
S=1;
M=min(M,ANS[i]);
cout<<M<<' ';
}
}
cout<<endl;
}
}
B Make Them Equal
非常非常有趣的题,简单思考易知,把从2到n的数字尽量全部转移到1上,再由1往2到n上补充数是最优解,只需要2n次,但可能会有情况,比如
a
99
=
98
a_{99}=98
a99=98时这里面的98就取不出来,需要1先给他一部分,让他达到取出的最低限度,然后取出,再由1还回去,这就是3步,但这样仍然WA了。
接下来的调试过程就只能说是小技巧了,比如1先给缺口小的单位补充数,用优先队列对于缺口进行排序等等,调着调着莫名其妙就A了,但仍然感觉不太对劲,比赛中我代码一定被Hack(笑)
#include<bits/stdc++.h>
#define io_opt ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define ll long long
using namespace std;
const int maxn=4e4+5;
int Start[maxn],l[maxn],ans[maxn],ANS[maxn],lu[5][maxn],CNT;
ll a[maxn];
struct E
{
int id,spare;
};
bool operator <(E x,E y)
{
if(x.spare==y.spare){
return x.id<y.id;
}
else return x.spare>y.spare;
}
priority_queue<E> P;
void insert(int x1,int x2,int v1)
{
++CNT;
lu[1][CNT]=x1;lu[2][CNT]=x2;lu[3][CNT]=v1;
}
int main()
{
io_opt;
int T,n;
cin>>T;
while(T--){
cin>>n;CNT=0;
ll tot=0;
for(int i=1;i<=n;i++){
cin>>a[i];
tot+=a[i];
}
if(tot%n!=0){
cout<<"-1"<<endl;
continue;
}
tot=tot/n;
for(int i=2;i<=n;i++){
int m=a[i]/i;
ll Q=1ll*m*i;
if(Q!=0){
if(a[i]-Q==0){
a[i]-=Q;a[1]+=Q;
insert(i,1,m);
}
else{
if(a[1]>=i-(a[i]-Q)){
insert(1,i,i-(a[i]-Q));
insert(i,1,m+1);
a[1]+=a[i];a[i]=0;
}
else{
a[i]-=Q;a[1]+=Q;
insert(i,1,m);
}
}
}
if(a[i]>0){
E CN;
CN.id=i;CN.spare=i-a[i];
P.push(CN);
}
}
int flag=1;
while(!P.empty()){
E CN=P.top();P.pop();
ll X_1=CN.id;
ll res=X_1-a[X_1];
if(a[1]<res){
flag=0;
break;
}
a[1]-=res;a[X_1]+=res;
insert(1,X_1,res);
a[1]+=X_1;a[X_1]-=X_1;
insert(X_1,1,1);
}
if(flag==0){
cout<<"-1"<<endl;
continue;
}
for(int i=2;i<=n;i++){
int X_1=tot-a[i];
a[1]-=X_1;a[i]+=X_1;
insert(1,i,X_1);
}
if(CNT>3*n){
cout<<"-1"<<endl;
continue;
}
else{
cout<<CNT<<endl;
for(int i=1;i<=CNT;i++){
cout<<lu[1][i]<<' '<<lu[2][i]<<' '<<lu[3][i]<<endl;
}
}
}
}
C. XOR Inverse
01字典树
构造01字典树,构造过程的每个节点记录01对(这一位为0的数字在前面,而后面的数字这一位是1则构成01对),和10对的数量,最后根据每一层的01对和10对的数量比较来选择这一位是0还是1
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int tol; //�ڵ����
const int MAXN=3e5;
int ch[32*MAXN][2]; //�ߵ�ֵ
int siz[2][32*MAXN],ans[40];
ll a01[40],a10[40];
void init()
{
tol=1;
ch[0][0]=ch[0][1]=0;
}
void insert(ll x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int v=(x>>i)&1;
//cout<<x<<' '<<i<<' '<<v<<' '<<u<<endl;
if(v==1){
a01[i]+=siz[0][u];
siz[1][u]++;
}
else{
a10[i]+=siz[1][u];
siz[0][u]++;
}
/*if(i==0){
cout<<x<<' '<<a01[i]<<' '<<a10[i]<<endl;
}*/
if(!ch[u][v])
{
ch[tol][0]=ch[tol][1]=0;
ch[u][v]=tol++;
}
u=ch[u][v];
}
}
int main()
{
int n;
scanf("%d",&n);
init();
for(int i=1;i<=n;i++){
ll cnt;
scanf("%lld",&cnt);
insert(cnt);
}
ll ans=0,num_inver=0;
for(int i=32;i>=0;i--){
//cout<<i<<' '<<a01[i]<<' '<<a10[i]<<endl;
if(a01[i]>=a10[i]){
ans=1ll*ans*2;
ans+=1ll*0;
num_inver+=1ll*a10[i];
}
else{
ans=1ll*ans*2;
ans+=1ll*1;
num_inver+=1ll*a01[i];
}
}
printf("%lld %lld\n",num_inver,ans);
}
D. Graph and Queries
反向建图
dfs序+并查集+线段树+反向建树
用的都是简单的算法,合起来却很有难度,网上都说是图上的经典问题,我却完全没有印象,看来做题还是少了
因为在询问过程中伴随着删边操作,并查集合并非常简单但删边操作很难复现在并查集上,所以采用反向并查集,反向建图,离线询问的方法将删边操作改为加边操作,最后通过dfs序和线段树的方法维护子树最大值,并支持单点修改和区间询问
这道题我也非常喜欢,运用一点数据结构但也不是阴间的数据结构,通过思维将传统数据结构进行组合求解,既有成就感又有思维的乐趣
E. Split
F. Showing Off
待补充