T1:
题目大意:
将 个字符串排序,使得任意 满足: 且 。
要求输出排序过程 (即每次交换哪两个字符串)。
解析:
构建出一棵字典树,那么以该树的任意一个dfs序输出都是一个合法的解。
然后我们只考虑一种简单的情况,即按照字典序输出,string类的sort可以方便完成这一任务。
考虑排序后输出一种合法的过程,那么我们可以对排序后的数列进行一次 的还原,输出过程即可。
时间复杂度:
Code
#include<bits/stdc++.h>
using namespace std;
namespace IO{
inline int Read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
void Get(){
freopen("block.in","r",stdin);
freopen("block.out","w",stdout);
}
}
#define Read() IO::Read()
struct Node{
string s;
int id;
}a[1000005];
bool cmp(Node x,Node y){
return x.s<y.s;
}
int m=0;
struct node{
int ans1,ans2;
}f[1000005];
signed main(){
IO::Get();
int n=Read();
for(register int i=1;i<=n;++i){
cin>>a[i].s;
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
for(register int i=1;i<=n;++i){
while(a[i].id!=i){
++m;
f[m].ans1=a[i].id,f[m].ans2=a[a[i].id].id;
swap(a[i],a[a[i].id]);
}
}
printf("%d\n",m);
for(register int i=1;i<=m;++i){
printf("%d %d\n",f[i].ans1,f[i].ans2);
}
return 0;
}
T2:
题目大意:
有 个数 ,每一秒你可以将任意一个数减少 ,每一秒每一个数都会自然减少 。求 秒后最大数的最小值。有多次询问。
解析:
我们要使得最大数最小,那么首先我们每秒减少的那个数必定是当前的最大数。
先忽略每个数的自然减少,在最后统计时加上即可。
考虑统计时二分答案,对于分出的一个最大数的最小值 (忽略自然减少),我们有每一个数所需的减少 的次数:
但这样做时间复杂度为 ,需要想办法优化。
还是从上面的式子开始:
前面的那部分可以预处理一个后缀和,后面部分可以离线用线段树做,也可以在线用主席树做。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,val[100005],sum[100005],t[100005],cnt;
namespace Tree{
struct node{
int lc,rc;
int x;
}seg[7500005];
void pushup(int o){
seg[o].x=seg[seg[o].lc].x+seg[seg[o].rc].x;
}
void modify(int &a,int l,int r,int pos,int v){
seg[++cnt]=seg[a];
a=cnt;
if(l==r){
seg[a].x+=v;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) modify(seg[a].lc,l,mid,pos,v);
if(mid<pos) modify(seg[a].rc,mid+1,r,pos,v);
pushup(a);
}
int query(int a,int l,int r,int nl,int nr){
if(!a) return 0;
if(nl<=l&&r<=nr) return seg[a].x;
if(nl>r||nr<l) return 0;
int mid=(l+r)>>1;
return query(seg[a].lc,l,mid,nl,nr)+query(seg[a].rc,mid+1,r,nl,nr);
}
}
int check(int y){
int res=0;
int p=lower_bound(val+1,val+n+1,y)-val;
res=sum[p]-(y/x)*(n-p+1);
res+=Tree::query(t[p],0,x,y%x+1,x);
return res;
}
int solve(int y){
int l=1,r=val[n];
while(l<r){
int mid=(l+r)>>1;
if(check(mid)>y) l=mid+1;
else r=mid;
}
return max(l-y,0ll);
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&x);
for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
sort(val+1,val+n+1);
for(int i=n;i>=1;i--){
sum[i]=sum[i+1]+val[i]/x;
t[i]=t[i+1];
Tree::modify(t[i],0,x,val[i]%x,1);
}
for(int i=1;i<=m;i++){
int y;scanf("%lld",&y);
printf("%lld\n",solve(y));
}
}
T3:
题目大意:
给一个长度为 字符串,包含 的整数,要在其中添加 个加号,求所有构成的表达式的值之和。
解析:
对于一个序列:
考虑每个数作为个位数,十位数等的贡献,我们对于第三个
,假设有
个加号,那么首先有:
除了当前的这个位置,其余位置都可以放加号,贡献为:
对于十位数的情况,我们有:
当前放加号的位置和前面的那个位置均不能放加号,贡献为:
以此类推,我们有了一个
的算法:
for(int i=1;i<=n;i++){
ll tmp=0;
for(int j=1;j<=min(n-i+1,n-k);j++){
if(j==n-i+1){
tmp+=c[n-j+1][k+1]*mul[j-1];
tmp%=Mod;
}
else{
tmp+=c[n-j][k]*mul[j-1];
tmp%=Mod;
}
}
ans+=tmp*a[i];
ans%=Mod;
}
cout<<ans<<endl;
下面考虑优化,非常不显然地打表+猜想可得,原式为:
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353,maxn=500100;
int ksm(int a,int b){
int res=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) res=1ll*res*a%mod;
return res;
}
int fac[maxn],invfac[maxn],f[maxn];
int C(int n,int m){return (n<0||n<m)?0:1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;}
int n,k;char s[500005];
signed main(){
for(int i=fac[0]=1;i<maxn;i++) fac[i]=1ll*fac[i-1]*i%mod;
invfac[maxn-1]=ksm(fac[maxn-1],mod-2);
for(int i=maxn-2;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
scanf("%d%d",&n,&k);scanf("%s",s+1);
int ans=0;
for(int i=n-1;i;i--) f[i]=(f[i+1]+1ll*ksm(10,n-i-1)*C(i-1,k-1)%mod)%mod;
for(int i=1;i<=n;i++) f[i]=(f[i]+1ll*ksm(10,n-i)*C(i-1,k)%mod)%mod;
for(int i=1;i<=n;i++) ans=(ans+1ll*f[i]*(s[i]-'0')%mod)%mod;
cout<<ans<<endl;
}