The Preliminary Contest for ICPC Asia Shanghai 2019

The Preliminary Contest for ICPC Asia Shanghai 2019 上海网络赛

A. Lightning Routing I

B. Light bulbs

题意:给定n盏灯,编号为0~n-1,灯的初始状态是灭的,给定一些区间,让区间内的灯的状态翻转,问最后的明灭情况
解法一:线段树:lazy+离散化
区间维护亮灯数量,这题挺卡内存的,所以离散化一下

#include <iostream>
#include <algorithm>
#include <cstdio>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
using namespace std;
const int maxn=1010,INF=0x3f3f3f3f;

int T,ST[maxn<<3],X[maxn<<1],lazy[maxn<<3];
int n,m;
pair<int,int> p[maxn];

void Push_up(int rt)
{
	ST[rt]=ST[ls]+ST[rs];
}

void Push_down(int rt,int L,int R)
{
	if(lazy[rt])
	{
		lazy[ls]^=lazy[rt];
		lazy[rs]^=lazy[rt];
		int mid=(L+R)>>1;
		ST[ls]=X[mid+1]-X[L]-ST[ls];
		ST[rs]=X[R+1]-X[mid+1]-ST[rs];
		lazy[rt]=0;
	}
}

void Build(int rt,int L,int R)
{
	ST[rt]=0,lazy[rt]=0;
	if(L==R)
		return;
	int mid=(L+R)>>1;
	Build(ls,L,mid);
	Build(rs,mid+1,R);
}

void Update(int rt,int l,int r,int L,int R)
{
	if(l<=L&&R<=r)
	{
		ST[rt]=X[R+1]-X[L]-ST[rt];
		lazy[rt]^=1;
		return;
	}
	Push_down(rt,L,R);
	int mid=(L+R)>>1;
	if(l<=mid)
		Update(ls,l,r,L,mid);
	if(r>mid)
		Update(rs,l,r,mid+1,R);
	Push_up(rt);
}

int main()
{
	scanf("%d",&T);
	int Case=0;
	while(T--)
	{
		scanf("%d%d",&n,&m);
		int l,r;
		for(int i=1;i<=m;++i)
		{
			scanf("%d%d",&p[i].first,&p[i].second);
			X[i*2-1]=p[i].first;
			X[i*2]=++p[i].second;
		} 		
		sort(X+1,X+1+2*m);
		int total=unique(X+1,X+1+2*m)-X-1;
		
		Build(1,1,total-1);		
		for(int i=1;i<=m;++i)
		{
			int l=lower_bound(X+1,X+1+total,p[i].first)-X;
			int r=lower_bound(X+1,X+1+total,p[i].second)-X-1;			
			Update(1,l,r,1,total-1);			 
		}
		printf("Case #%d: %d\n",++Case,ST[1]);
	}
	return 0;
}

解法二:差分
对每一个区间做差分标记,然后从左往右扫一下累积和为奇数的数量

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=1010,INF=0x3f3f3f3f;
int T,n,m;
pair<int,int> p[maxn<<1]; 

int main()
{
	scanf("%d",&T);
	int Case=0;
	while(T--)
	{
		int cnt=0,ans=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;++i)
		{
			int l,r; 
			scanf("%d%d",&l,&r);
			p[++cnt]={l,1};
			p[++cnt]={r+1,-1};
		}
		sort(p+1,p+1+cnt);
		
		int last=-1,sum=0;
		for(int i=1;i<=cnt-1;++i)
		{
			sum+=p[i].second;
			if(last!=p[i+1].first)
			{
				if(sum&1)
					ans+=p[i+1].first-p[i].first;	
                last=p[i].first;
			}						
		}
		printf("Case #%d: %d\n",++Case,ans);
	}
	return 0;
}

C. Triple

题意:一个三元组(A,B,C),满足任意两数之差小于等于第三个数,换句话说,任意两数之和大于等于第三个数。给定A、B、C的三个数组,问符合的三元组有多少
思路:FFT+暴力

  1. 题目中数据说了,最多20组数据的N>1000,所以对于小数据暴力,对于大数据用FFT处理
  2. 直接正向处理不太好,我们反向处理,对于任意两个数组使它们两两相加,得到和。那么在另一个数组中,大于这个和的数量乘以这个和的数量,就是不合法的数量
  3. 所以总体的做法,就是处理出三个数组两两相加的相同和的数量,处理出剩余一个数组的前缀和,最后遍历这些和累计答案
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+10,INF=0x3f3f3f3f;
const int mod=1e9+7;
const double PI=acos(-1.0);

struct Complex
{
	double x,y;
	Complex(double x1=0.0,double y1=0.0)
	{
		x=x1,y=y1;
	}
};

Complex operator+(Complex a,Complex b)
{
	return {a.x+b.x,a.y+b.y};
}

Complex operator-(Complex a,Complex b)
{
	return {a.x-b.x,a.y-b.y};
}

Complex operator*(Complex a,Complex b)
{
	return {a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}

struct FFT
{
	int total,digit,rev[maxn<<2];
	Complex a[maxn<<2],b[maxn<<2];
	
	void init(int len)
	{
		total=1,digit=0;
		while(total<=len)
			total<<=1,digit++;
		for(int i=0;i<total;++i)
		{
			rev[i]=(rev[i>>1]>>1)|(i&1)<<(digit-1);
			a[i].x=0.0,a[i].y=0.0;
			b[i].x=0.0,b[i].y=0.0;
		}	
	}
	
	void fft(Complex *A,int f)
	{
		for(int i=0;i<total;++i)
			if(i<rev[i])
				swap(A[i],A[rev[i]]);
			
		for(int mid=1;mid<total;mid<<=1)
		{
			Complex Wn={cos(PI/mid),f*sin(PI/mid)};
			int len=mid*2;
			for(int p=0;p<total;p+=len)
			{
				Complex Wk={1,0};
				for(int k=0;k<mid;++k)
				{
					Complex x=A[p+k],y=Wk*A[p+k+mid];
					A[p+k]=x+y;
					A[p+k+mid]=x-y; 	
					Wk=Wk*Wn;	
				}		
			}	
		}	
	}
	
	void calc()
	{
		fft(a,1),fft(b,1);
		for(int i=0;i<total;++i)
			a[i]=a[i]*b[i];
		fft(a,-1);
	}
}F;

int T,n,Case;
int maxa,maxb,maxc;
int a[maxn],b[maxn],c[maxn];
int numa[maxn],numb[maxn],numc[maxn];
int pref_a[maxn],pref_b[maxn],pref_c[maxn];
int sumab[maxn*4],sumac[maxn*4],sumbc[maxn*4];

void init(int n)
{
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		numa[a[i]]++;
		maxa=max(maxa,a[i]);
	}
	
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&b[i]);
		numb[b[i]]++;
		maxb=max(maxb,b[i]);
	}	
	
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&c[i]);
		numc[c[i]]++;
		maxc=max(maxc,c[i]);
	}
	
	for(int i=1;i<=maxa;++i)
		pref_a[i]=pref_a[i-1]+numa[i];
	
	for(int i=1;i<=maxb;++i)
		pref_b[i]=pref_b[i-1]+numb[i];
	
	for(int i=1;i<=maxc;++i)
		pref_c[i]=pref_c[i-1]+numc[i];
}

ll solve1(int n)
{	
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			sumab[a[i]+b[j]]++;
			sumac[a[i]+c[j]]++;
			sumbc[b[i]+c[j]]++;
		}
	}
	
	ll ans=1ll*n*n*n;
	int mx=max(maxa,max(maxb,maxc));
	for(int i=1;i<=mx;++i)
	{
		if(i<maxc)	ans-=1ll*sumab[i]*(pref_c[maxc]-pref_c[i]);
		if(i<maxb)	ans-=1ll*sumac[i]*(pref_b[maxb]-pref_b[i]);
		if(i<maxa)	ans-=1ll*sumbc[i]*(pref_a[maxa]-pref_a[i]);
	}
	return ans;
}

ll solve2(int n)
{	
	F.init(maxa+maxb);
	for(int i=1;i<=maxa;++i)
		F.a[i]={numa[i],0};
	for(int i=1;i<=maxb;++i)
		F.b[i]={numb[i],0};
	F.calc();
	for(int i=0;i<F.total;++i)
		sumab[i]=(ll)(F.a[i].x/F.total+0.5);
    
	F.init(maxa+maxc);
	for(int i=1;i<=maxa;++i)
		F.a[i]={numa[i],0};
	for(int i=1;i<=maxc;++i)
		F.b[i]={numc[i],0};
	F.calc();
	for(int i=0;i<F.total;++i)
		sumac[i]=(ll)(F.a[i].x/F.total+0.5);
  
	F.init(maxb+maxc);
	for(int i=1;i<=maxb;++i)
		F.a[i]={numb[i],0};
	for(int i=1;i<=maxc;++i)
		F.b[i]={numc[i],0};	
	F.calc();
	for(int i=0;i<F.total;++i)
		sumbc[i]=(ll)(F.a[i].x/F.total+0.5);

	ll ans=1ll*n*n*n;
	int mx=max(maxa,max(maxb,maxc));
	for(int i=1;i<=mx;++i)
	{
		if(i<maxc)	ans-=1ll*sumab[i]*(pref_c[maxc]-pref_c[i]);
		if(i<maxb)	ans-=1ll*sumac[i]*(pref_b[maxb]-pref_b[i]);
		if(i<maxa)	ans-=1ll*sumbc[i]*(pref_a[maxa]-pref_a[i]);
	}
	return ans;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);			
		init(n);							
		ll ans;
		if(n<=1000)
			ans=solve1(n);
		else
			ans=solve2(n);
		printf("Case #%d: %lld\n",++Case,ans);	
		
		memset(numa,0,sizeof(int)*(maxa+1));
		memset(numb,0,sizeof(int)*(maxb+1));
		memset(numc,0,sizeof(int)*(maxc+1));
		memset(pref_a,0,sizeof(int)*(maxa+1));
		memset(pref_b,0,sizeof(int)*(maxb+1));
		memset(pref_c,0,sizeof(int)*(maxc+1));
		memset(sumab,0,sizeof(int)*(maxa+maxb+1));
		memset(sumac,0,sizeof(int)*(maxa+maxc+1));
		memset(sumbc,0,sizeof(int)*(maxb+maxc+1));		
	}   
	return 0;
}

D. Counting Sequences I

题意:给定一个n,求 ∑ i = 1 n a i = ∏ i = 1 n a i \sum_{i=1}^n a_i=\prod_{i=1}^n a_i i=1nai=i=1nai合法的序列数
思路:从2开始爆搜,每次搜的时候有两条路可以走,一个是当前位置的数自增1,一个往下一个位置搜。最后要小心处理组合数

#include <iostream>
#include <algorithm>
#include <cstdio>
#define ll long long
using namespace std;
const int mod=1e9+7;

ll qpow(ll base,ll n,ll mod)
{
	ll ret=1;
	while(n)
	{
		if(n&1)
			ret=ret*base%mod;
		base=base*base%mod;
		n>>=1;
	}
	return ret;
}

const int N=3005;
ll fac[N+10],finv[N+10];

void init()
{
	fac[0]=fac[1]=1;
	for(int i=2;i<=N;++i)
		fac[i]=fac[i-1]*i%mod;
	finv[N]=qpow(fac[N],mod-2,mod);
	for(int i=N-1;i>=0;--i)
		finv[i]=finv[i+1]*(i+1)%mod; 
}

ll dfs(int limit,int pos,int n,int prod,int sum,int cnt,ll now)
{
	if(n+1==pos)
	{
		if(prod==sum)
			return now*finv[cnt]%mod*fac[n]%mod;
		else
			return 0;
	}
	if(prod==sum+n-pos+1)
		return now*finv[cnt]%mod*finv[n-pos+1]%mod*fac[n]%mod;
	if(limit>n)
		return 0;
	if(prod>sum+n-pos+1)
		return 0;
	if(prod*limit>sum+limit+(n-pos))
		return 0;
	ll ans=0;
	ans+=dfs(limit,pos+1,n,prod*limit,sum+limit,cnt+1,now);
	ans+=dfs(limit+1,pos,n,prod,sum,0,now*finv[cnt]%mod);
	return ans%mod;
}

int main()
{
	init();
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		ll ans=dfs(2,1,n,1,0,0,1);
		printf("%lld\n",ans);
	}
	return 0;
}

E. Counting Sequences II

题意:给定n个位置,对于每一个数 a i a_i ai,都满足 1 ≤ a i ≤ m 1\le a_i \le m 1aim,并且如果 a i a_i ai是偶数的话, a i a_i ai出现的次数是偶数次,求排列的个数
思路:求的是排列,所以是指数型生成函数。在[1,m]的范围内,相当于有m个因子,每个因子分别代表对 1 、 2 、 3 、 4 、 … 、 m 1、2、3、4、\dots、m 1234m各自的限制。其中偶数有 f l o o r ( m 2 ) floor(\frac m2) floor(2m)个,奇数有 m − f l o o r ( m 2 ) m-floor(\frac m2) mfloor(2m),设 t = f l o o r ( m 2 ) t=floor(\frac m2) t=floor(2m),可以得到指数型生成函数
g ( x ) = ( 1 + x + x 2 2 ! + x 3 3 ! + …   ) m − t ( 1 + x 2 2 ! + x 4 4 ! + …   ) t g(x)=(1+x+\frac {x^2}{2!}+\frac {x^3}{3!}+\dots)^{m-t}(1+\frac {x^2}{2!}+\frac {x^4}{4!}+\dots)^{t} g(x)=(1+x+2!x2+3!x3+)mt(1+2!x2+4!x4+)t
化简可得:
g ( x ) = e x ( m − t ) ( e x + e − x 2 ) t = e x ( m − t ) ∑ i = 0 t C t i e x ( t − i ) e − x i 2 t g(x)=e^{x(m-t)}(\frac {e^x+e^{-x}}2)^t=\frac{e^{x(m-t)}\sum_{i=0}^tC_t^ie^{x(t-i)}e^{-xi}}{2^t} g(x)=ex(mt)(2ex+ex)t=2tex(mt)i=0tCtiex(ti)exi
g ( x ) = ∑ i = 0 t C t i e ( m − 2 i ) x 2 t = ∑ n = 0 ∑ i = 0 t C t i ( m − 2 i ) n 2 t x n n ! g(x)=\frac {\sum_{i=0}^tC_t^ie^{(m-2i)x}}{2^t}=\sum_{n=0}\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t}\frac {x^n}{n!} g(x)=2ti=0tCtie(m2i)x=n=02ti=0tCti(m2i)nn!xn
因此可以得到 a n = ∑ i = 0 t C t i ( m − 2 i ) n 2 t a_n=\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t} an=2ti=0tCti(m2i)n a n a_n an就是答案

#include <iostream>
#include <algorithm>
#include <cstdio>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define ll long long
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
int T;
ll n,m;

ll QuickPower(ll base,ll n,ll mod)
{
	ll ret=1;
	while(n)
	{
		if(n&1)
			ret=(ret*base)%mod;
		n>>=1;
		base=(base*base)%mod;
	}
	return ret;
}

const int N=2e5+10;
ll fac[N+10],finv[N+10];
void init()
{
	fac[0]=fac[1]=1;
	for(int i=2;i<=N;++i)
		fac[i]=fac[i-1]*i%mod;
	finv[N]=QuickPower(fac[N],mod-2,mod);
	for(int i=N-1;i>=0;--i)
		finv[i]=finv[i+1]*(i+1)%mod;
}

int main()
{
	init();
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld",&n,&m);
		ll ans=0;
		int t=m/2;
		for(int i=0;i<=t;++i)
		{
			ans=(ans+fac[t]*finv[i]%mod*finv[t-i]%mod*QuickPower(m-2*i,n,mod)%mod)%mod;
		}
		ll x=QuickPower(2,t,mod);
		ans=ans*QuickPower(x,mod-2,mod)%mod;
		printf("%lld\n",ans);	
	}
	return 0;
}

F. Rhyme scheme

G. Substring

H. Luhhy’s Matrix

I. Debug

J. Stone game

题意:一共有n堆石子,选择任意堆,组成数量s满足s>=sum-s&&s-a[i]<=sum-s
思路:01背包转移
每次只要删去最小的做判断就好了,学到一种新方法叫“退背包”
设dp[i][j]表示前i个物品恰好凑成体积j的方案数。
具体做法就是 先把dp做了,把物品从小到大排序,然后把当前的a[i]退了。统计符合条件的答案:j>=sum-j&&j-a[i]<=sum-j,其实此时枚举的j是j+a[i],带入上式就是:

j + a [ i ] > = s u m − ( j + a [ i ] ) & & j + a [ i ] − a [ i ] < = s u m − ( j + a [ i ] ) j+a[i]>=sum-(j+a[i])\&\&j+a[i]-a[i]<=sum-(j+a[i]) j+a[i]>=sum(j+a[i])&&j+a[i]a[i]<=sum(j+a[i])

所以 j j j 要满足: s u m / 2 − a [ i ] < = j < = s u m − ( j + a [ i ] ) sum/2-a[i]<=j<=sum-(j+a[i]) sum/2a[i]<=j<=sum(j+a[i])

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int mod=1e9+7;
int t,n,a[305];
int dp[150010];

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		memset(dp,0,sizeof(dp));
		scanf("%d",&n);
		int sum=0;
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]),sum+=a[i];	
		sort(a+1,a+1+n);
		dp[0]=1;
		for(int i=1;i<=n;++i)
			for(int j=sum;j>=a[i];--j)
				dp[j]=(dp[j]+dp[j-a[i]])%mod;
		ll ans=0;
		for(int i=1;i<=n;++i)
		{
			for(int j=a[i];j<=sum;++j)
				dp[j]=(dp[j]-dp[j-a[i]]+mod)%mod;
			for(int j=max((sum+1)/2-a[i],0);j<=sum-j-a[i];++j)
				ans=(ans+dp[j])%mod;
		}
		printf("%lld\n",ans);	
	}
	return 0;
}

解法二:也是01背包转移,但其实不需要用到退背包,从大到小遍历,最直接就可以统计符合条件的答案了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int mod=1e9+7;
int t,n;
int a[505],dp[150010];

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		memset(dp,0,sizeof(dp));		
		scanf("%d",&n);
		int sum=0;
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]),sum+=a[i];	
		sort(a+1,a+1+n,greater<int>());	
		dp[0]=1;
		ll ans=0;
		for(int i=1;i<=n;++i)
		{
			for(int j=sum;j>=a[i];--j)
			{
				dp[j]=(dp[j]+dp[j-a[i]])%mod;
				if(j>=sum-j&&j-a[i]<=sum-j)
					ans=(ans+dp[j-a[i]])%mod;
			}
		}		
		printf("%lld\n",ans); 		
	}
	return 0;
}

K. Peekaboo

题意:一共有三个人,一个人在原点,距离其余两人的距离为a、b,其余两人的距离为c,求在二维坐标上符合条件的,且其余两人都在整点上的位置,答案有多组,按字典序输出
思路:求圆上整点的题,困扰了我好久,主要是被本原勾股数影响到了,以为枚举的时候也要两个奇数什么的
做法:很明显就是以a为半径画圆,求出整点的位置,再以b为半径画圆,求出整点的个数,判断距离是否等于c。思路很简单,细节真的有点难搞
细节:不要重复枚举,s枚举到 r d \frac {r}{d} dr就可以了,因为 s 2 + t 2 = 2 r d s^2+t^2=\frac {2r}d s2+t2=d2r,s再从 r d \frac rd dr 继续往上枚举的话,就重复了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
ll t,a,b,c;
vector<pll> v1,v2;

struct Node
{
	ll x1,y1,x2,y2;
};
vector<Node> ans;

ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}

void check(ll r,ll d,vector<pll> &v)
{
	for(ll s=1;s*s<=r/d;++s)
	{
		double t1=sqrt(2*r/d-s*s);
		ll t=t1;
		if(t==t1&&gcd(s,t)==1&&s!=t)
		{
			//ll y = r - s*s*d;
            //ll x = (ll)sqrt(r*r - y*y);
			ll x=s*t*d;
			ll y=d*(s*s-t*t)/2;
			v.push_back({-x,-y}),v.push_back({-x,y});
			v.push_back({x,-y}),v.push_back({x,y});	
		}
	}
}

void solve(ll r,vector<pll> &v)
{
	v.push_back({-r,0}),v.push_back({0,-r});
	v.push_back({0,r}),v.push_back({r,0});
	for(ll d=1;d*d<=2*r;++d)
	{
		if(2*r%d==0)
		{
			check(r,d,v);
			if(d*d!=2*r)
				check(r,2*r/d,v);
		}
	}
    sort(v.begin(),v.end(),[](pll a,pll b)
    {
        if(a.first==b.first)
            return a.second<b.second;
        return a.first<b.first;	
    });
}

int main()
{
	cin>>t;
	while(t--)
	{
		cin>>a>>b>>c;
		v1.clear(),v2.clear(),ans.clear();
		solve(a,v1);
		solve(b,v2);	
		
		for(int i=0;i<v1.size();++i)
		{
			for(int j=0;j<v2.size();++j)
			{			
				ll dis=(v1[i].first-v2[j].first)*(v1[i].first-v2[j].first)+
				(v1[i].second-v2[j].second)*(v1[i].second-v2[j].second);
				if(dis==c*c)
					ans.push_back({v1[i].first,v1[i].second,v2[j].first,v2[j].second});	
			}
		}
		printf("%d\n",ans.size());
		for(int i=0;i<ans.size();++i)
			printf("%lld %lld %lld %lld\n",ans[i].x1,ans[i].y1,ans[i].x2,ans[i].y2);		 
	}
	return 0;
}

L. Digit sum

题意:给定一个十进制的数n,和进制b,求1~n以b进制表示时,各个位上的数之和。 S 10 ( 233 ) = 2 + 3 + 3 = 8 S_{10}(233)=2+3+3=8 S10(233)=2+3+3=8,求 ∑ i = 1 n S b ( i ) \sum_{i=1}^nS_b(i) i=1nSb(i)
思路:官方题解都说预处理了,还在乱七八糟推公式,b最大是10,预处理前缀和就好了,真的得好好看看数据范围,说不定真就暴力就完事了。

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=1e6;
int t,b; 
int C[11][N+5];

int f(int a,int b)
{
	int ans=0;
	while(b)
	{
		ans+=b%a;
		b/=a;
	}
	return ans;
}

int lowbit(int x)
{
	return x&(-x);
}

void add(int i,int pos,int val)
{
	for(int x=pos;x<=N;x+=lowbit(x))
		C[i][x]+=val;
}

int getsum(int pos,int i)
{
	int res=0;
	for(int x=pos;x>0;x-=lowbit(x))
		res+=C[i][x];
	return res;
}

void init()
{
	for(int i=2;i<=10;++i)
		for(int j=1;j<=N;++j)
			add(i,j,f(i,j));
}

int main()
{
	init();
	int Case=0;
	scanf("%d",&t);
	while(t--)
	{
		int n,b;
		scanf("%d%d",&n,&b);
		int ans=getsum(n,b);
		printf("Case #%d: %d\n",++Case,ans);
	}
	return 0;
}

你可能感兴趣的:(比赛)