19.11.12考后总结

T1:

题目大意:

n n n个字符串排序,使得任意 i < j < k i < j < k i<j<k 满足: l c p ( s i , s k ) ≤ l c p ( s i , s j ) lcp(si, sk) ≤ lcp(si, sj ) lcp(si,sk)lcp(si,sj) l c p ( s i , s k ) ≤ l c p ( s j , s k ) lcp(si, sk) ≤ lcp(sj , sk) lcp(si,sk)lcp(sj,sk) n ≤ 1 0 6 n\le10^6 n106

l c p : 最 长 公 共 前 缀 lcp:最长公共前缀 lcp:

要求输出排序过程 (即每次交换哪两个字符串)。

解析:

构建出一棵字典树,那么以该树的任意一个dfs序输出都是一个合法的解。

然后我们只考虑一种简单的情况,即按照字典序输出,string类的sort可以方便完成这一任务。

考虑排序后输出一种合法的过程,那么我们可以对排序后的数列进行一次 O ( n ) O(n) O(n)的还原,输出过程即可。

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

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:

题目大意:

n n n 个数 t i t_i ti,每一秒你可以将任意一个数减少 x x x,每一秒每一个数都会自然减少 1 1 1。求 s s s 秒后最大数的最小值。有多次询问。 n ≤ 1 0 5 n\le10^5 n105

解析:

我们要使得最大数最小,那么首先我们每秒减少的那个数必定是当前的最大数。

先忽略每个数的自然减少,在最后统计时加上即可。

考虑统计时二分答案,对于分出的一个最大数的最小值 v v v(忽略自然减少),我们有每一个数所需的减少 x x x的次数: m a x ( ⌈ ( t i − v ) / x ⌉ , 0 ) max(\lceil (t_i-v)/x\rceil,0) max((tiv)/x,0)

但这样做时间复杂度为 O ( n 2 ) O(n^2) O(n2),需要想办法优化。

还是从上面的式子开始:

⌈ ( t i − v ) / x ⌉ = ⌈ ( ⌊ t i x ⌋ + { t i x } ) − ( ⌊ v x ⌋ + { v x } ) ⌉ \lceil (t_i-v)/x\rceil=\lceil(\lfloor\frac{t_i}{x}\rfloor+\{\frac{t_i}{x}\})-(\lfloor\frac{v}{x}\rfloor+\{\frac{v}{x}\})\rceil (tiv)/x=(xti+{xti})(xv+{xv})
     = ⌈ ⌊ t i x ⌋ − ⌊ v x ⌋ ⌉ + ⌈ { t i x } − { v x } ⌉ \qquad\qquad\space\space\space\space=\lceil\lfloor\frac{t_i}{x}\rfloor-\lfloor\frac{v}{x}\rfloor\rceil+\lceil\{\frac{t_i}{x}\}-\{\frac{v}{x}\}\rceil     =xtixv+{xti}{xv}
     = ⌈ ⌊ t i x ⌋ − ⌊ v x ⌋ ⌉ + [ t i % x > v % x ] \qquad\qquad\space\space\space\space=\lceil\lfloor\frac{t_i}{x}\rfloor-\lfloor\frac{v}{x}\rfloor\rceil+[t_i\%x>v\%x]     =xtixv+[ti%x>v%x]
前面的那部分可以预处理一个后缀和,后面部分可以离线用线段树做,也可以在线用主席树做。

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:

题目大意:

给一个长度为 n n n字符串,包含 0 − 9 0-9 09的整数,要在其中添加 k k k个加号,求所有构成的表达式的值之和。 n ≤ 5 × 1 0 5 n\le5\times10^5 n5×105

解析:

对于一个序列:
2 3 3 3 3 3 3 2\qquad3\qquad3\qquad3\qquad3\qquad3\qquad3 2333333
考虑每个数作为个位数,十位数等的贡献,我们对于第三个 3 3 3,假设有 4 4 4个加号,那么首先有:
2 3 3    +    3 3 3 3 2\qquad3\qquad3\space\space+\space\space3\qquad3\qquad3\qquad3 233  +  3333
除了当前的这个位置,其余位置都可以放加号,贡献为:
C 5 3 × 1 0 0 × 3 C_5^3\times10^0\times3 C53×100×3
对于十位数的情况,我们有:
2 3 3 3    +    3 3 3 2\qquad3\qquad3\qquad3\space\space+\space\space3\qquad3\qquad3 2333  +  333
当前放加号的位置和前面的那个位置均不能放加号,贡献为:
C 4 3 × 1 0 1 × 3 C_4^3\times10^1\times3 C43×101×3
以此类推,我们有了一个 O ( n 2 ) O(n^2) O(n2)的算法:

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;

下面考虑优化,非常不显然地打表+猜想可得,原式为:
f i = 1 0 n − i − 1 × C i − 1 k − 1 + [ i = = n ] × ( 1 0 n − i × C i − 1 k ) f_i=10^{n-i-1}\times C_{i-1}^{k-1}+[i==n]\times(10^{n-i}\times C_{i-1}^{k}) fi=10ni1×Ci1k1+[i==n]×(10ni×Ci1k)
a n s = ∑ i = 1 n f i × s i ans=\sum_{i=1}^{n}f_i\times s_i ans=i=1nfi×si

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值