cqoi2018
题解:
很多模板题
第一次写莫队还比较顺利
除了把排序的cmp写错。。(还第一次遇到)
这题分块也可以
先预处理出g[i][j]代表前i个块,颜色为j的有多少种
f[i][j]表示i-j的块能构成多少对
处理的方法就是f[i][j-1]+j块内和j与j之前
算答案的时候即整块+两个单独块内部和两个单独块与整块之间
因为分块有几个地方都是$n\sqrt{n}$的 所以分块的常数比莫队要来的大
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for (int i=h;i<=t;i++) #define dep(i,t,h) for (int i=t;i>=h;i--) #define ll long long #define me(x) memset(x,0,sizeof(x)) namespace IO { char ss[1<<24],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++; } template<class T>void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48); while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } char sr[1<<24],z[20]; int Z,C=-1; template<class T>void wer(T x) { if (x<0) sr[++C]='-',x=-x; while (z[++Z]=x%10+48,x/=10); while (sr[++C]=z[Z],--Z); } IL void wer1() { sr[++C]=' '; } IL void wer2() { sr[++C]='\n'; } template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;} template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} template<class T>IL T MAX(T x,T y){return x>y?x:y;} template<class T>IL T MIN(T x,T y){return x<y?x:y;} }; using namespace IO; const int N=1e5+10; const int M=350; int n,m,k,block,q; int a[N],pos[N],b[N],now1[N],now2[N]; ll ans2[N]; struct re{ int a,b,c; }p[N]; ll ans=0; bool cmp(re x,re y) { return pos[x.a]<pos[y.a]||(pos[x.a]==pos[y.a]&&x.b<y.b); } IL void insert1(int x,int y) { if (y==1) { now2[b[x]]+=y; now1[b[x-1]]+=y; } ans+=now2[b[x-1]^k]*y; if (y==-1) { now2[b[x]]+=y; now1[b[x-1]]+=y; } } IL void insert2(int x,int y) { if (y==1) { now2[b[x]]+=y; now1[b[x-1]]+=y; } ans+=now1[b[x]^k]*y; if (y==-1) { now2[b[x]]+=y; now1[b[x-1]]+=y; } } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); read(q); read(k); rep(i,1,n) read(a[i]); block=sqrt(n); m=(n-1)/block+1; rep(i,1,n) pos[i]=(i-1)/block+1; rep(i,1,q) read(p[i].a),read(p[i].b),p[i].c=i; sort(p+1,p+q+1,cmp); rep(i,1,n) b[i]=b[i-1]^a[i]; rep(i,p[1].a,p[1].b) { now1[b[i-1]]++; ans+=now1[b[i]^k]; now2[b[i]]++; } ans2[p[1].c]=ans; rep(i,2,q) { if (p[i].a>p[i-1].a) rep(j,p[i-1].a,p[i].a-1) insert1(j,-1); else dep(j,p[i-1].a-1,p[i].a) insert1(j,1); if (p[i].b>p[i-1].b) rep(j,p[i-1].b+1,p[i].b) insert2(j,1); else dep(j,p[i-1].b,p[i].b+1) insert2(j,-1); ans2[p[i].c]=ans; } rep(i,1,q) wer(ans2[i]),wer2(); fwrite(sr,1,C+1,stdout); return 0; }
题目上面画个矩形下面用点真的是傻逼
要是是矩形的画就比较麻烦 要判断4条边是否与那个线相交
可以写特殊处理 当然也可以直接用计算几何里的线段与线段相交判定了
要是直线的画直接叉积就可以了
预处理一下再状压dp
不太卡常
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for (int i=h;i<=t;i++) #define dep(i,t,h) for (int i=t;i>=h;i--) #define ll long long #define me(x) memset(x,0,sizeof(x)) namespace IO { char ss[1<<24],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++; } template<class T>void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48); while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } char sr[1<<24],z[20]; int Z,C=-1; template<class T>void wer(T x) { if (x<0) sr[++C]='-',x=-x; while (z[++Z]=x%10+48,x/=10); while (sr[++C]=z[Z],--Z); } IL void wer1() { sr[++C]=' '; } IL void wer2() { sr[++C]='\n'; } template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;} template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} template<class T>IL T MAX(T x,T y){return x>y?x:y;} template<class T>IL T MIN(T x,T y){return x<y?x:y;} }; using namespace IO; double ee=1.0000000000000000; const double eps=1e-9; const int N=(1<<20)+1000; const int mo=100000007; int n; int f[21][N]; int p[40][40]; struct re{ int x,y; }a[40]; IL void js(register int &x,register int y) { x=(x+y>mo)?x+y-mo:x+y; } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); rep(i,1,n) { read(a[i].x); read(a[i].y); } rep(i,1,n) rep(j,1,n) if (i!=j) rep(k,1,n) if (k!=i&&k!=j) { /*if (a[i].x==a[j].x) { if (a[k].x==a[i].x&&a[k].y>=MIN(a[i].y,a[j].y)&&a[k].y<=MAX(a[i].y,a[j].y)) p[i][j]|=(1<<(k-1)); } else { double now=ee*(a[j].y-a[i].y)/(a[j].x-a[i].x)*(a[k].x-a[i].x)+a[i].y; if (a[k].x<=MAX(a[i].x,a[j].x)&&a[k].x>=MIN(a[i].x,a[j].x)&&now+eps>a[k].y&&now<a[k].y+1) p[i][j]|=(1<<(k-1)); }*/ if (a[k].x<=MAX(a[i].x,a[j].x)&&a[k].x>=MIN(a[i].x,a[j].x)&&a[k].y<=MAX(a[i].y,a[j].y)&&a[k].y>=MIN(a[i].y,a[j].y) &&(a[k].x-a[i].x)*(a[j].y-a[k].y)-(a[j].x-a[k].x)*(a[k].y-a[i].y)==0) p[i][j]|=1<<(k-1); } rep(i,1,n) f[i][1<<(i-1)]=1; int l=(1<<n)-1; int ans=0; rep(i,1,l) rep(j,1,n) if (f[j][i]) { rep(k,1,n) if (((i>>(k-1))&1)==0&&(p[j][k]&(~i))==0) js(f[k][i|(1<<(k-1))],f[j][i]); if (__builtin_popcount(i)>=4) js(ans,f[j][i]); } wer(ans); fwrite(sr,1,C+1,stdout); return 0; }
[CQOI2018]交错序列
这可能是六题里唯一一题不是模板的题
首先最暴力的做法
就是$f[i][j][0/1]$表示前i个数有j个1且当前是0/1的方案数
复杂度$O(n^2)$
发现这个dp方程比较简单,考虑直接用数学方法求解
先放1,然后再插空 答案就是$C(n-k+1,k)*k^a*(n-k)^b$
这个东西$nlogn$ 因为模数比较小 所以组合数需要用lucas定理计算
据游记所说这种做法好像并不能卡过?
晚上写
然后注意到这题a,b比较小
考虑去展开$k^a*(n-k)^b$
会发现我们只需要维护$k^{1~(a+b)}$就可以了
然后$(k+1)^x$这个东西又可以用关于k的次方递推
于是就可以上矩阵快速幂了
时间复杂度$(a+b)^3*logn$