总结
基本上就是暴力分,思维还是需要提升
A. 老夫
分析
将所有元素按照 (b) 从小到大排序
枚举 (c) 的值,将 (b) 的值小于 (c) 的元素按照 (a) 从小到大排序
设一共有 (m) 个这样的元素
那么答案就是 (max((m-i+1) imes a[i]))
发现每次加入一个新的元素就是把之前所有 (a) 的值小于这个元素的 (i) 的贡献加上 (a[i])
线段树不大好维护
可以对序列进行分块构建凸包
令 (ans=sum+a[i] imes k)
把 (a[i]) 看成 (x),把 (sum) 看成 (y)
在凸包上二分即可
新加入一个元素时把当前元素所在的凸包暴力重构
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e5+5;
struct jie{
int vala,valb,rk;
}b[maxn];
bool cmp1(rg jie aa,rg jie bb){
return aa.valb<bb.valb;
}
bool cmp2(rg jie aa,rg jie bb){
return aa.vala<bb.vala;
}
int n,w,maxa,maxb,blo,shuyu[maxn],d[maxn],tp,tr[maxn];
inline int lb(rg int xx){
return xx&-xx;
}
void ad(rg int wz){
for(rg int i=wz;i<=n;i+=lb(i)) tr[i]++;
}
int cx(rg int wz){
rg int nans=0;
for(rg int i=wz;i>0;i-=lb(i)) nans+=tr[i];
return nans;
}
struct Node{
int x,id;
long long y;
Node(){}
Node(rg int aa,rg long long bb,rg int cc){
x=aa,y=bb,id=cc;
}
friend bool operator < (const Node& A,const Node& B){
if(A.x==B.x) return A.y<B.y;
return A.x<B.x;
}
}sta[maxn],que[maxn];
std::vector<Node> g[maxn],tmp[maxn];
double getxl(rg Node aa,rg Node bb){
if(aa.x==bb.x){
if(bb.y>aa.y) return 1e18;
else if(bb.y<aa.y) return -1e18;
else return 0;
}
return (double)(bb.y-aa.y)/(double)(bb.x-aa.x);
}
void insert(rg int num){
rg int id=shuyu[b[num].rk];
tp=0;
g[id].clear();
if(d[id]) for(rg int i=0;i<tmp[id].size();i++) tmp[id][i].y+=1LL*d[id]*tmp[id][i].x;
d[id]=0;
ad(b[num].rk);
tmp[id].push_back(Node(b[num].vala,1LL*b[num].vala*(cx(n)-cx(b[num].rk-1)),b[num].rk));
for(rg int i=0;i<tmp[id].size();i++) if(tmp[id][i].id<b[num].rk) tmp[id][i].y+=tmp[id][i].x;
for(rg int i=0;i<tmp[id].size();i++) sta[++tp]=tmp[id][i];
std::sort(sta+1,sta+tp+1);
rg int tail=1;
que[1]=sta[1];
for(rg int i=2;i<=tp;i++){
while(tail>1 && getxl(que[tail],sta[i])>=getxl(que[tail-1],que[tail])){
tail--;
}
que[++tail]=sta[i];
}
for(rg int i=1;i<=tail;i++) g[id].push_back(que[i]);
for(rg int i=1;i<id;i++) d[i]++;
}
long long js(){
rg long long nans=0;
for(rg int i=1;i<=shuyu[n];i++){
if(g[i].size()==0) continue;
rg int l=1,r=g[i].size(),mids;
while(l<r){
mids=(l+r)>>1;
if(getxl(g[i][mids-1],g[i][mids])<=-1.0*d[i]) r=mids;
else l=mids+1;
}
l--;
nans=std::max(nans,g[i][l].y+1LL*d[i]*g[i][l].x);
}
return nans;
}
int main(){
n=read(),w=read();
for(rg int i=1;i<=n;i++){
b[i].vala=read(),b[i].valb=read();
maxa=std::max(maxa,b[i].vala);
maxb=std::max(maxb,b[i].valb);
}
maxb++;
std::sort(b+1,b+n+1,cmp2);
for(rg int i=1;i<=n;i++) b[i].rk=i;
std::sort(b+1,b+n+1,cmp1);
blo=sqrt(n);
for(rg int i=1;i<=n;i++) shuyu[i]=(i-1)/blo+1;
rg int now=1;
for(rg int i=1;i<=maxb;i++){
while(now<=n && b[now].valb<i){
insert(now);
now++;
}
printf("%lld ",js()+1LL*i*w*(n-now+1));
}
printf("
");
return 0;
}
B. 打算
分析
如果按照正常的坐标进行移动,那么在某次操作中,横纵坐标只有一个能够发生变化
不好处理
考虑将坐标 ((x,y)) 转化成 ((x+y,x-y)),这样就将两维独立了出来
横纵坐标可以任意加 (1) 减 (1),并且和原来的坐标变换一一对应
令 (s_i) 表示 (i) 时刻的位置
(s_i=s_{t_i mod L}+s_{L}*lfloor frac{t_i}{L} floor)
可以写成 (s_{t_i mod L}=As_L+B) 的形式
按照 (s) 的 下标排序,把相邻两个等式之间作差
根据时间和路程的关系 (|s_i-s_{i-1}| leq t_i-t_{i-1}),可以解得 (s_L) 的范围
回代即可求出其它的值
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define rg register
const int maxn=2e6+5;
const long double orz=1.0;
int n,l;
long long t[maxn],x[maxn],y[maxn];
struct Node{
long long a,b,t;
Node(){}
Node(rg long long aa,rg long long bb,rg long long cc){
a=aa,b=bb,t=cc;
}
friend bool operator < (const Node& A,const Node& B){
return A.t<B.t;
}
}jlx[maxn],jly[maxn];
void bye(){
printf("NO
");
std::exit(0);
}
int ansx[maxn],ansy[maxn];
void solve(rg Node A[],rg long long &L,rg long long &R,rg int ans[]){
L=-1e18,R=1e18;
for(rg int i=2;i<=n;i++){
rg long long nl=A[i-1].t-A[i].t,nr=A[i].t-A[i-1].t,na=A[i].a-A[i-1].a,nb=A[i].b-A[i-1].b;
if(na==0){
if(nb<nl || nb>nr) bye();
} else if(na<0){
L=std::max(L,(long long)std::ceil(orz*(nr-nb)/na));
R=std::min(R,(long long)std::floor(orz*(nl-nb)/na));
} else {
L=std::max(L,(long long)std::ceil(orz*(nl-nb)/na));
R=std::min(R,(long long)std::floor(orz*(nr-nb)/na));
}
}
if((L+l)&1LL) L++;
if((R+l)&1LL) R--;
if(L>R) bye();
A[1].b+=L*A[1].t;
for(rg int i=2;i<=n;i++){
A[i].b+=L*A[i].a;
if(A[i].t==A[i-1].t) continue;
rg long long tmp=A[i-1].t,cz=A[i].b-A[i-1].b;
while(cz>0){
ans[tmp]=1;
tmp++;
cz--;
}
while(cz<0){
ans[tmp]=0;
tmp++;
cz++;
}
while(tmp+2<=A[i].t){
ans[tmp]=1;
tmp++;
ans[tmp]=0;
tmp++;
}
if(tmp!=A[i].t) bye();
}
}
int main(){
scanf("%d%d",&n,&l);
rg long long tmpx,tmpy;
for(rg int i=1;i<=n;i++){
scanf("%lld%lld%lld",&t[i],&x[i],&y[i]);
if((t[i]&1LL)!=((x[i]+y[i])&1LL)) bye();
tmpx=x[i]+y[i];
tmpy=x[i]-y[i];
x[i]=tmpx,y[i]=tmpy;
}
for(rg int i=1;i<=n;i++){
jlx[i]=Node(-1LL*t[i]/l,x[i],t[i]%l);
jly[i]=Node(-1LL*t[i]/l,y[i],t[i]%l);
}
jlx[++n]=Node(0,0,0),jly[++n]=Node(0,0,0);
jlx[++n]=Node(1,0,l),jly[++n]=Node(1,0,l);
std::sort(jlx+1,jlx+1+n),std::sort(jly+1,jly+1+n);
rg long long lx,rx,ly,ry;
solve(jlx,lx,rx,ansx);
solve(jly,ly,ry,ansy);
for(rg int i=0;i<l;i++){
if(ansx[i] && ansy[i]) printf("R");
else if(ansx[i] && !ansy[i]) printf("U");
else if(!ansx[i] && ansy[i]) printf("D");
else printf("L");
}
printf("
");
return 0;
}
C. 报复社会
分析
考虑字符集只有 (2) 的情况
设这两种字符分别为 (1,2)
设 (g_i=sum1_i-sum2_i),则要求对于任意 (i,j in [0,n]),(g_i) 和 (g_j) 的差值不超过 (k)
因为 (g_0=0),所以合法的区间为 ([-k,0],[-k+1,1] cdots [0,k])
然而这样会使一些方案算重,也就是 ([−k+1,0]) 等区间被算了两次,所以要减掉一次
对于长度更小的区间 ([i,i+l]) ,会被计算 ((k-l+1)-(k-l)=1) 次,恰好被容斥掉了,所以不用考虑
每一次转移的系数是一样的,所以可以用矩阵快速幂优化
考虑扩展到字符集为 (3) 的情况
设 (g1_i=cnt1_i-cnt3_i,g2_i=cnt2_i-cnt3_i,g3_i=cnt1_i-cnt3_i)
(g_1) 合法的区间为 ([l_1,l_1+d_1]),(g_2) 合法的区间为 ([l_2,l_2+d_2]),(g_3) 合法的区间为 ([l_3,l_3+d_3])
设 (f[o][i][j]) 为当前考虑到第 (o) 个位置,(g1) 和最小值的差为 (i),(g2) 和最小值的差为 (j) 的方案数
也就是把 (i) 看作 (l_1+i),把 (j) 看作 (l_2+j)
(g3) 可以由 (g1) 和 (g2) 推得
为了使 (g_3) 的范围合法,必须满足 (l_3 leq l_1+i-(l_2+j) leq l_3+d_3)
剩下的就和字符集为 (2) 的一样了
拿子集反演容斥,奇减偶加
一共有 (2^3) 种情况,但是本质不同的情况只有 (4) 种
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define rg register
const int maxn=39,mod=998244353;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
long long n;
int m,cnt,a[maxn],b[maxn][maxn];
void mul1(){
int tmp[maxn][maxn];
memset(tmp,0,sizeof(tmp));
for(rg int i=1;i<=cnt;i++){
for(rg int j=1;j<=cnt;j++){
for(rg int k=1;k<=cnt;k++){
tmp[i][j]=addmod(tmp[i][j],mulmod(b[i][k],b[k][j]));
}
}
}
memcpy(b,tmp,sizeof(tmp));
}
void mul2(){
int tmp[maxn];
memset(tmp,0,sizeof(tmp));
for(rg int i=1;i<=cnt;i++){
for(rg int j=1;j<=cnt;j++){
tmp[i]=addmod(tmp[i],mulmod(a[j],b[j][i]));
}
}
memcpy(a,tmp,sizeof(tmp));
}
int id[maxn][maxn],ans;
int solve(rg int l1,rg int d1,rg int l2,rg int d2,rg int l3,rg int d3){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(id,0,sizeof(id));
cnt=0;
for(rg int i=0;i<=d1;i++){
for(rg int j=0;j<=d2;j++){
rg int tmp=(l1+i)-(l2+j);
if(tmp>=l3 && tmp<=l3+d3) id[i][j]=++cnt;
}
}
for(rg int i=0;i<=d1;i++){
for(rg int j=0;j<=d2;j++){
if(id[i][j]){
if(id[i+1][j]) b[id[i][j]][id[i+1][j]]++;
if(id[i][j+1]) b[id[i][j]][id[i][j+1]]++;
if(i && j && id[i-1][j-1]) b[id[i][j]][id[i-1][j-1]]++;
}
}
}
a[id[-l1][-l2]]=1;
rg long long tmp=n;
while(tmp){
if(tmp&1LL) mul2();
mul1();
tmp>>=1LL;
}
rg int nans=0;
for(rg int i=1;i<=cnt;i++) nans=addmod(nans,a[i]);
return nans;
}
int main(){
scanf("%lld%d",&n,&m);
for(rg int i=-m;i<=0;i++){
for(rg int j=-m;j<=0;j++){
for(rg int k=-m;k<=0;k++){
ans=addmod(ans,solve(i,m,j,m,k,m));
}
}
}
for(rg int i=-m;i<=0;i++){
for(rg int j=-m;j<=0;j++){
for(rg int k=-m+1;k<=0;k++){
ans=delmod(ans,mulmod(3,solve(i,m,j,m,k,m-1)));
}
}
}
for(rg int i=-m;i<=0;i++){
for(rg int j=-m+1;j<=0;j++){
for(rg int k=-m+1;k<=0;k++){
ans=addmod(ans,mulmod(3,solve(i,m,j,m-1,k,m-1)));
}
}
}
for(rg int i=-m+1;i<=0;i++){
for(rg int j=-m+1;j<=0;j++){
for(rg int k=-m+1;k<=0;k++){
ans=delmod(ans,solve(i,m-1,j,m-1,k,m-1));
}
}
}
printf("%d
",ans);
return 0;
}