题目描述
http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=825
题解
签到题。
排个序枚举小的两条然后二分出第三条就好了。
其实排序之后只要看连续的三条就行了。
代码
#include
#define N 1005
using namespace std;
int n,s[N],ans;
int main()
{
int p;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)scanf("%d",&s[i]);
sort(s+1,s+n+1);ans=-1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
p=upper_bound(s+1,s+n+1,s[i]+s[j]-1)-s-1;
while(s[p]>=s[i]+s[j]&&p)p--;
if(p<=j)continue;
ans=max(ans,s[i]+s[j]+s[p]);
}
printf("%d\n",ans);
}
return 0;
}
题解
当时脑子抽了写了个平衡树艹过去的。
其实用双向链表搞搞判判就好了。
贴的是平衡树的代码。
代码
#include
#define ll long long
#define N 400010
using namespace std;
int n,Q,cnt,rt[N],L=2147483647;
struct node{
int x;int lc,rc,tag,size;
bool operator <(const node &p)const{
int k=2000000207;
return (ll)((k+=(k<<2)+1)&L)*(size+p.size)<(ll)(size)*L;
}
void clear(){x=0;lc=0;rc=0;tag=0;size=0;}
}t[N*10];
inline int get()
{
char ch;int v;
while(!isdigit(ch=getchar()));v=ch-48;
while(isdigit(ch=getchar()))v=v*10+ch-48;
return v;
}
class functional_trape
{
public:
void pushdown(int x)
{
if(!t[x].tag)return;
int lc=t[x].lc,rc=t[x].rc;
if(lc)swap(t[lc].lc,t[lc].rc),t[lc].tag^=1;
if(rc)swap(t[rc].lc,t[rc].rc),t[rc].tag^=1;
t[x].tag=0;
}
void split(int x,int &a,int &b,int k)
{
if(!x){a=0;b=0;return;}
pushdown(x);
if(t[t[x].lc].size>=k)
{
b=++cnt;t[b]=t[x];
split(t[x].lc,a,t[b].lc,k);
t[b].size-=t[a].size;
}
else
{
a=++cnt;t[a]=t[x];
split(t[x].rc,t[a].rc,b,k-t[t[x].lc].size-1);
t[a].size-=t[b].size;
}
}
int merge(int a,int b)
{
if(!a)return b;
if(!b)return a;
int x=++cnt;
if(t[a]x]=t[a];
t[x].rc=merge(t[a].rc,b);
t[x].size+=t[b].size;
}
else
{
pushdown(b);t[x]=t[b];
t[x].lc=merge(a,t[b].lc);
t[x].size+=t[a].size;
}
return x;
}
void insert(int &x,int w,int val)
{
int p=++cnt;t[p].x=val;t[p].size=1;
if(!w)x=merge(p,x);
else x=merge(x,p);
}
void erase(int &x,int w)
{
int a=0,b=0;
if(!w)split(x,a,b,1),x=b;
else split(x,a,b,t[x].size-1),x=a;
}
void reverse(int x)
{
swap(t[x].lc,t[x].rc);t[x].tag^=1;
}
int qry(int x,int w)
{
int k=w?t[x].size:1;
while(1)
{
pushdown(x);
if(t[t[x].lc].size+1==k)break;
if(t[t[x].lc].size>=k)x=t[x].lc;
else k-=t[t[x].lc].size+1,x=t[x].rc;
}
return t[x].x;
}
bool empty(int x){return t[x].size==0;}
}T;
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
int tp,u,v,w,val;
while(~scanf("%d%d",&n,&Q))
{
for(int i=1;i<=cnt;i++)t[i].clear();cnt=0;
for(int i=1;i<=n;i++)rt[i]=0;
while(Q--)
{
tp=get();
if(tp==1)
{
u=get();w=get();val=get();
T.insert(rt[u],w,val);
}
if(tp==2)
{
u=get();w=get();
if(T.empty(rt[u])){printf("-1\n");continue;}
printf("%d\n",T.qry(rt[u],w));
T.erase(rt[u],w);
}
if(tp==3)
{
u=get();v=get();w=get();
if(w)T.reverse(rt[v]);
rt[u]=T.merge(rt[u],rt[v]);rt[v]=0;
}
}
}
return 0;
}
题解
我们肯定会把若干段1剪开放在前缀。
一段1被剪开的代价是2(除非是开头的1或结尾的1)。
注意到每次操作最后可以有一段1不剪右边,放在1和0的分界点。记这种情况为特殊情况
所以定义f[i]表示剪i次,没有特殊情况的最长前缀。
g[i]表示剪i次,有特殊情况的最长前缀。注意特殊情况只能出现一次。
然后转移一波就好了。
代码
#include
#define N 10005
using namespace std;
int n,k,tot,len,ans,s[N],f[N],g[N],flag,tag;
int main()
{
int len;char ch;
while(~scanf("%d%d",&n,&k))
{
for(int i=0;i<=k;i++)f[i]=0,g[i]=0;
for(int i=1;i<=tot;i++)s[i]=0;
len=0;tot=0;ans=0;flag=0;tag=0;
for(int i=1;i<=n;i++)
{
scanf(" %c",&ch);
if(i==1&&ch=='1')tag=1;
if(ch=='1')len++;
else if(len)s[++tot]=len,len=0;
}
if(len)s[++tot]=len,flag=1;
int st=1;
if(tag)g[0]=s[1],st=2;
for(int i=st;i<=tot;i++)
{
for(int j=k;j>=1;j--)
{
if(i==tot&&flag)
{
f[j]=max(f[j],f[j-1]+s[i]);
g[j]=max(g[j],g[j-1]+s[i]);
}
else
{
if(j>1)
{
f[j]=max(f[j],f[j-2]+s[i]);
g[j]=max(g[j],g[j-2]+s[i]);
}
g[j]=max(g[j],f[j-1]+s[i]);
}
}
}
for(int i=0;i<=k;i++)ans=max(ans,max(f[i],g[i]));
printf("%d\n",ans);
}
return 0;
}
题解
一开始以为是两排,一排n个座位,想了好久不会做。
后来发现是一排,总共2*n个座位,然后就会做了。
答案乘了个阶乘,所以求期望实际上是求方案数。
定义状态f[i][j]表示前i对情侣,有j对连在一起的方案数。
转移,考虑新加一对情侣对之前的影响。
新加一对情侣,可能增加一对相邻的情侣,或不变,或减少一对,或减少两对。
具体的转移式见代码。
这个题要先把dp离线算出来再做,不然会超时。
代码
#include
#define mod 998244353
#define ll long long
#define N 1005
using namespace std;
int n,m;ll f[N][N],ans;
int main()
{
f[0][0]=1;
for(int i=0;i<1000;i++)
for(int j=0;j<=i;j++)
{
f[i+1][j]=(f[i+1][j]+f[i][j]*(2*j+(i+i-j+1)*(i+i-j)))%mod;
f[i+1][j+1]=(f[i+1][j+1]+f[i][j]*2*(i+i+1-j))%mod;
if(j>=1)f[i+1][j-1]=(f[i+1][j-1]+f[i][j]*2*j*(i+i-j+1))%mod;
if(j>=2)f[i+1][j-2]=(f[i+1][j-2]+f[i][j]*j*(j-1))%mod;
}
while(~scanf("%d%d",&n,&m))
{
ll sum=1;ans=0;
for(int i=0;i<=n;i++,sum=sum*m%mod)
ans=(ans+f[n][i]*sum)%mod;
cout<
}
return 0;
}
当时就过了这四题,然后就跑了。
题解
题目说的是每次让一个点失效,我们逆向思维,考虑每次让一个点生效。
这题关键就是这个逆向思维了。
然后每次生效一个点,我们就把它和它的左边右边合并。
合并的时候可以用启发式合并,这样就是log方的了。
至于区间和的计算,区间 [i,j] 和等价于 sum1[j]-sum[i-1],或 sum2[i]-sum2[j-1]。
所以对于每段,维护一下sum[i],在启发式合并的时候再另一边二分找的最近点就好了。
代码
#include
#define inf 999999999999999999ll
#define iter multiset::iterator
#define ll long long
#define N 100010
using namespace std;
int n,k,fa[N],s[N],p[N],flag[N];
ll sum1[N],sum2[N],ans[N],res;
multisett1[N],t2[N],t3[N],t4[N];
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
int pos,tmp;
x=find(x);y=find(y);
if(!flag[x]||!flag[y])return;
if(t1[x].size()<=t1[y].size())
{
for(iter it=t2[x].begin();it!=t2[x].end();++it)
{
iter p=t4[y].lower_bound((*it)-k);
if(p!=t4[y].end())res=min(res,abs((*it)-(*p)-k));
if(p!=t4[y].begin())--p,res=min(res,abs((*it)-(*p)-k));
}
}
else
{
for(iter it=t1[y].begin();it!=t1[y].end();++it)
{
iter p=t3[x].lower_bound((*it)-k);
if(p!=t3[x].end())res=min(res,abs((*it)-(*p)-k));
if(p!=t3[x].begin())--p,res=min(res,abs((*it)-(*p)-k));
}
swap(x,y);
}
fa[x]=y;
for(iter it=t1[x].begin();it!=t1[x].end();++it)
t1[y].insert(*it);
for(iter it=t2[x].begin();it!=t2[x].end();++it)
t2[y].insert(*it);
for(iter it=t3[x].begin();it!=t3[x].end();++it)
t3[y].insert(*it);
for(iter it=t4[x].begin();it!=t4[x].end();++it)
t4[y].insert(*it);
t1[x].clear();t2[x].clear();
t3[x].clear();t4[x].clear();
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
sum2[n+1]=0;res=inf;
for(int i=1;i<=n;i++)fa[i]=i,flag[i]=0;
for(int i=1;i<=n;i++)scanf("%d",&s[i]);
for(int i=1;i<=n;i++)
{
sum1[i]=sum1[i-1]+s[i];
t1[i].clear();t1[i].insert(sum1[i]);
t3[i].clear();t3[i].insert(sum1[i-1]);
}
for(int i=n;i;i--)
{
sum2[i]=sum2[i+1]+s[i];
t2[i].clear();t2[i].insert(sum2[i]);
t4[i].clear();t4[i].insert(sum2[i+1]);
}
for(int i=1;i<=n;i++)scanf("%d",&p[i]);
for(int i=n;i;i--)
{
flag[p[i]]=1;
if(p[i]>1)merge(p[i]-1,p[i]);
if(p[i]1);
res=min(res,(ll)abs(s[p[i]]-k));
ans[i]=res;
}
for(int i=1;i<=n;i++)printf("%I64d\n",ans[i]);
}
return 0;
}