题意:给定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;
}
题意:一个三元组(A,B,C),满足任意两数之差小于等于第三个数,换句话说,任意两数之和大于等于第三个数。给定A、B、C的三个数组,问符合的三元组有多少
思路:FFT+暴力
#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;
}
题意:给定一个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;
}
题意:给定n个位置,对于每一个数 a i a_i ai,都满足 1 ≤ a i ≤ m 1\le a_i \le m 1≤ai≤m,并且如果 a i a_i ai是偶数的话, a i a_i ai出现的次数是偶数次,求排列的个数
思路:求的是排列,所以是指数型生成函数。在[1,m]的范围内,相当于有m个因子,每个因子分别代表对 1 、 2 、 3 、 4 、 … 、 m 1、2、3、4、\dots、m 1、2、3、4、…、m各自的限制。其中偶数有 f l o o r ( m 2 ) floor(\frac m2) floor(2m)个,奇数有 m − f l o o r ( m 2 ) m-floor(\frac m2) m−floor(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+…)m−t(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(m−t)(2ex+e−x)t=2tex(m−t)∑i=0tCtiex(t−i)e−xi
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)=2t∑i=0tCtie(m−2i)x=n=0∑2t∑i=0tCti(m−2i)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=2t∑i=0tCti(m−2i)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;
}
题意:一共有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/2−a[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;
}
题意:一共有三个人,一个人在原点,距离其余两人的距离为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;
}
题意:给定一个十进制的数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;
}