LOJ 2347~2351
BZOJ上只有其中2道: 4273,4279
寒冬暖炉
dp可以推个柿子把转移优化到 O(1) O ( 1 ) ,再套个wqs二分把状态数优化到 O(nlogn) O ( n l o g n )
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ld long double
using namespace std;
inline void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;
const ld eps = 1e-12;
int n,K;
int a[maxn],b[maxn],ti[maxn];
struct node
{
int k; ld x;
}f[maxn];
int dp(ld mid)
{
f[0]=(node){0,0.0};
int nown=0;
for(int i=1;i<=n;i++)
{
f[i].k=f[nown].k+1;
f[i].x=f[nown].x-b[nown]+(ld)a[i]+mid;
if(f[i].x-b[i]return f[n].k;
}
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
read(n); read(K);
for(int i=1;i<=n;i++) read(ti[i]);
for(int i=0;i<=n;i++) a[i]=ti[i]+1,b[i]=ti[i+1];
ld l=-ti[n],r=ti[n];
while(r-l>eps)
{
ld mid=(l+r)/2.0;
int k=dp(mid);
if(k==K) { l=mid;break; }
if(kelse l=mid;
}
printf("%.0Lf\n",f[n].x-l*K);
return 0;
}
美术展览
从小到大枚举Amax,最优的Amin可以 O(1) O ( 1 ) 维护
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
void read(ll &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10ll)+=c-'0';
}
const int maxn = 510000;
int n;
struct node
{
ll a,b;
friend inline bool operator <(const node x,const node y){return x.aint main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) read(a[i].a),read(a[i].b);
sort(a+1,a+n+1);
ll sum=0,pn=a[1].a,ans=0;
for(int i=1;i<=n;i++)
{
sum+=a[i].b;
ans=max(ans,pn-a[i].a+sum);
pn=max(pn,a[i+1].a-sum);
}
printf("%lld\n",ans);
return 0;
}
团子制作
将RGB定义为012,将一串团子在1处计数
发现不在同一对角线的1,他们串的团子不可能相交,同一对角线只有相邻的1有可能相交,对每一个对角线做一个dp,复杂度是线性的
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline void up(int &a,const int &b){if(aconst int maxn = 3100;
int n,m,ans;
int a[maxn][maxn];
int Matchl(int i,int j)
{
return a[i][j-1]==0&&a[i][j]==1&&a[i][j+1]==2;
}
int Matchc(int i,int j)
{
return a[i-1][j]==0&&a[i][j]==1&&a[i+1][j]==2;
}
int f[2][3];
char str[maxn];
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
memset(a,-1,sizeof a);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",str+1);
for(int j=1;j<=m;j++) a[i][j]=str[j]=='R'?0:(str[j]=='G'?1:2);
}
int ans=0;
for(int s=2;s<=n+m;s++)
{
memset(f,0,sizeof f); int now=0;
for(int j=max(1,s-n);j<=m&&j+1<=s;j++)
{
int i=s-j;
now=!now;
for(int l=0;l<3;l++)
{
int &temp=f[!now][l];
up(f[now][0],temp);
if(l!=2&&Matchl(i,j)) up(f[now][1],temp+1);
if(l!=1&&Matchc(i,j)) up(f[now][2],temp+1);
temp=0;
}
}
up(f[now][0],f[now][1]);
up(f[now][0],f[now][2]);
ans+=f[now][0];
}
printf("%d\n",ans);
return 0;
}
月票购买
一开始不会做,想了想部分分发现好像会了qaq
不难证明最终答案里U到V的最短路径,和S到T的月票线路只会有一段相交
枚举相交那一段的方向,也就是S->T和T->S两个方向,给每条有可能在ST最短路上的边定向,规定如果这条边要走0费用,一定要沿这个方向走
然后每个点拆3个点:还没相交,在相交段内,已过了相交段,跑Dijskral
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;
const int maxm = 410000;
ll ans;
int n,m,S,T,U,V;
int e[maxm][3],ec[maxm];
struct Graph
{
struct edge{int y,c,i,nex;}a[maxm]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c,const int i)
{
a[++len]=(edge){y,c,i,fir[x]};fir[x]=len;
}
struct node
{
ll x; int i;
friend inline bool operator <(const node &x,const node &y){return x.x>y.x;}
}; priority_queueq;
ll dis1[maxn],dis2[maxn],d[maxn<<1];
void Dij(int st,ll dis[])
{
for(int i=1;i<=n;i++) dis[i]=LLONG_MAX;
dis[st]=0; q.push((node){0ll,st});
while(!q.empty())
{
const node now=q.top(); q.pop();
int x=now.i; if(dis[x]!=now.x) continue;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(dis[y]>dis[x]+a[k].c)
dis[y]=dis[x]+a[k].c,q.push((node){dis[y],y});
}
}
void solve(int st,ll D,ll d1[],ll d2[])
{
for(int i=1;i<=m;i++) ec[i]=-1;
for(int x=1;x<=n;x++)
{
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(d1[x]for(int i=1;i<=n*3;i++) d[i]=LLONG_MAX;
d[U]=0; q.push((node){d[U],U});
while(!q.empty())
{
const node now=q.top(); q.pop();
int x=now.i; if(d[x]!=now.x) continue;
int use=(x-1)/n; x=(x-1)%n+1;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
{
if(use!=2&&ec[a[k].i]==(xif(d[y+n]>now.x) d[y+n]=now.x,q.push((node){d[y+n],y+n});
}
int nu=use==1?2:use;
if(d[y+nu*n]>now.x+a[k].c)
d[y+nu*n]=now.x+a[k].c,q.push((node){d[y+nu*n],y+nu*n});
}
}
for(int i=0;i<3;i++) ans=min(ans,d[i*n+V]);
}
}g;
void Solve()
{
ans=LLONG_MAX;
g.solve(S,g.dis1[T],g.dis1,g.dis2);
g.solve(T,g.dis2[S],g.dis2,g.dis1);
}
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
read(n); read(m);
read(S); read(T);
read(U); read(V);
for(int i=1;i<=m;i++)
{
int x,y,c; read(x),read(y),read(c);
g.ins(x,y,c,i); g.ins(y,x,c,i);
e[i][0]=x,e[i][1]=y,e[i][2]=c;
}
g.Dij(S,g.dis1); g.Dij(T,g.dis2);
Solve();
printf("%lld\n",ans);
return 0;
}
毒蛇越狱
关于通配符的处理,一种方法是用子集和处理,但用子集和有个问题,就是询问串中要求为1的位置可能会算到0的情况,我们可以套一个容斥,但这个容斥的复杂度是单次 O(2L) O ( 2 L ) 的,太高了无法接受
观察发现1的个数,0的个数,?的个数最小值<=6,我们可以分三类,1的个数<=6时做子集和的容斥,0的个数<=6的时候,类似1的,我们可以另外做一个反向的子集和然后容斥,?的个数<=6时我们可以直接枚举所有匹配的情况统计和
总复杂度 O(2L+26q) O ( 2 L + 2 6 q )
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define cal(x) __builtin_popcount(x)
using namespace std;
const int maxn = (1<<20)+20;
int n,m,al;
int s[maxn],f1[maxn],f0[maxn];
char str[maxn];
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
scanf("%d%d",&n,&m); al=1<scanf("%s",str);
for(int i=0;i'0';
for(int i=0;ifor(int t=1<0;jif(!(j>>i&1))
{
f1[j+t]+=f1[j];
f0[j]+=f0[j+t];
}
while(m--)
{
scanf("%s",str);
int x=0,y=0,z=0;
for(int i=0;i1]=='1'?x|=1<1]=='0'?y|=1<1<int ans=0;
if(cal(x)<=6)
{
for(int i=x;;i=(i-1)&x)
{
(cal(x^i)&1)?ans-=f1[i|z]:ans+=f1[i|z];
if(!i) break;
}
}
else if(cal(y)<=6)
{
for(int i=y;;i=(i-1)&y)
{
(cal(i)&1)?ans-=f0[i|x]:ans+=f0[i|x];
if(!i) break;
}
}
else
{
for(int i=z;;i=(i-1)&z)
{
ans+=s[i|x];
if(!i) break;
}
}
printf("%d\n",ans);
}
return 0;
}