2015山东省选一轮总结

其实省选一轮过去已经有一段时间了,(蒟蒻山东rank 49,卡线到了二轮。。。)但因为先修课的原因,一直未能作总结。。。

DAY 1:这一天的暴力分75分,我拿到了55分。

T1的20分暴力比较好拿,正解是对每两个块之间的元素交换blabla后dfs,总之没听懂。

UPD:看了一下网上的题解,其实正解思路也还蛮简单,穷举不同2^i长度意义下的逆序对,如果超过两个,那么说明这个方法不可能成功。如果是一个或两个,那么分情况讨论后交换,继续dfs。同时需要一个蛮显然的结论,当一个操作序列长度为n时,无论怎样调换操作顺序,都是合法解,因此答案加上fac[n];code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct hp{
	int kind,len;
	int a[5000];
}opt[15];
int a[5000],ans=0,n;
int mi[15]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384};
int fac[15];
void swapi(int x,int y,int len)
{
	int i;
	for (i=0;i<len;++i)
	  swap(a[x+i],a[y+i]);
}
void dfs(int step,int now)
{
	int i,j,top=0;
	int stack[3]={0,0,0};
	if (now==n+1)
	  {
	    ans+=fac[step-1];
	    return;
	  }
	for (i=1;i<=mi[n];i+=opt[now+1].len)
	  {
	    if (a[i+opt[now].len-1]+1!=a[i+opt[now].len])
		  {
		    if (top==2) return;
		    stack[++top]=i;
		  }   
	  }
	if (top==0) {dfs(step,now+1); return;}
	if (top==1)
	  {
	  	i=stack[1]; 
	    if (a[i+opt[now+1].len-1]+1==a[i])
	      {
		    swapi(i,i+opt[now].len,opt[now].len);
		    dfs(step+1,now+1);
		    swapi(i,i+opt[now].len,opt[now].len);
	      }
		return;	
	  }
	if (top==2)
	  {
	    i=stack[1]; j=stack[2];
	    if (a[i+opt[now].len-1]+1==a[j+opt[now].len]&&a[j+opt[now].len-1]+1==a[i+opt[now].len])
	      {
	        swapi(i,j,opt[now].len);
	        dfs(step+1,now+1);
	        swapi(i,j,opt[now].len);
	        swapi(i+opt[now].len,j+opt[now].len,opt[now].len);
	        dfs(step+1,now+1);
	        swapi(i+opt[now].len,j+opt[now].len,opt[now].len);
	      }
	    if (a[j+opt[now].len-1]+1==a[i]&&a[j+opt[now+1].len-1]+1==a[i+opt[now].len])
	      {
	        swapi(i,j+opt[now].len,opt[now].len);
	        dfs(step+1,now+1);
	        swapi(i,j+opt[now].len,opt[now].len);
	      }
	    if (a[i+opt[now].len-1]+1==a[j]&&a[i+opt[now+1].len-1]+1==a[j+opt[now].len])
	      {
	        swapi(j,i+opt[now].len,opt[now].len);
	        dfs(step+1,now+1);
	        swapi(j,i+opt[now].len,opt[now].len);
	      }
	    return;
	  }
}
int main()
{
	int t,i,j;
	scanf("%d",&n);
	for (i=1;i<=mi[n];++i)
	  scanf("%d",&a[i]);
	fac[0]=1;
	for (i=1;i<=n;++i)
	  fac[i]=fac[i-1]*i;
	for (i=1;i<=n;++i)
	  {
	    t=0;
	    for (j=1;j<=mi[n];j+=mi[i-1])
	      opt[i].a[++t]=j;
	    opt[i].kind=t;
	    opt[i].len=mi[i-1];
	  }
	opt[n+1].len=mi[n];
	dfs(1,1);
	printf("%d\n",ans);
}

T2的35分暴力也还好,前20分乱搞即可,后15分因为是链状数据,在其上套一个线段树即可,但考试的时候未考虑是Win下评测,爆栈了两次。正解是虚树+欧拉路径+LCA,没听懂,不过zky学长的dfs序也蛮好理解的,每次处理出dfs序最左边的藏宝点与最右边的藏宝点,两者之间距离*2输出即可。维护需要处理出每个点的在dfs序中的最左最右位置。

T3的20分暴力考试没有想到,但其实是一个简单的DP,60%的数据需要用到矩阵乘法,100%的数据需要FFT。

Upd:夏令营时又一次听到了60%解法,终于明白了。DP方程比较简单 f[i][x]=f[i-1][x*ni[s[i]] 1<=i<=nums 然后我们可以套用快速幂的思想,发现可以在O(m^2)的是时间内由k推到2k,总的复杂度也就是O(logn*m^2),code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define P 1004535809
using namespace std;
struct hp{
	long long f[8001];
};
hp e,ans;
int n,m,x,nums;
hp cheng(hp a,hp b)
{
	int i,j;
	hp c;
	for (i=0;i<=m-1;++i)
	  for (j=0;j<=m-1;++j)
	    c.f[(i*j)%m]=(c.f[(i*j)%m]+(a.f[i]*b.f[j])%P)%P;
	return c;
}
hp work(int x)
{
	hp t;
	if (x==1)
	  return e;
	t=work(x/2);
	if (x%2==0) return cheng(t,t);
	else return cheng(cheng(t,t),e);
}
int main()
{
	int i,t;
	scanf("%d%d%d%d",&n,&m,&x,&nums);
	for (i=1;i<=nums;++i)
	  {
	    scanf("%d",&t);
	    e.f[t]=1;
      }
    ans=work(n);
    printf("%lld\n",ans.f[x]);
}

DAY 2:这一天的暴力分90分,我只拿到了20分。

T1其实非常非常简单,二分加最大流即可,但是考试的时候一直在向费用流方向考虑,2h30min就浪费在了各种胡思乱想中。。。

code(PS:不知道为啥从0开始建点就WA(连样例都过不了),从1开始就AC):

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define inf 0x7fffffffffffffffLL
using namespace std;
struct hp{
    int u,v;
    long long c;
}a[10000];
int e,ai[51],bi[51],n,m,next[10000],cur[200],pre[200],lev[200],gap[200],point[200];
long long l,r; 
int pic[51][51];
long long sum=0;
void add(int x,int y,long long c)
{
    e++; next[e]=point[x]; cur[x]=point[x]=e;
    a[e].u=x; a[e].v=y; a[e].c=c;
    e++; next[e]=point[y]; cur[y]=point[y]=e;
    a[e].u=y; a[e].v=x; a[e].c=0;
}
long long ISAP(int vs,int vt)
{
    int u,v,i,minl;
    long long maxf=0,aug; bool f;
    memset(pre,0,sizeof(pre));
    memset(gap,0,sizeof(gap));
    memset(lev,0,sizeof(lev));
    gap[0]=vt-vs+1; u=vs;
    while (lev[vs]<vt)
      {
        f=false;
        for (v=cur[u];v!=0;v=next[v])
          if (a[v].c>0&&lev[u]==lev[a[v].v]+1)
            {cur[u]=v; f=true; break;}
        if (f)
          {
            pre[a[v].v]=v;
            u=a[v].v;
            if (u==vt)
              {
                aug=inf;
                for (i=v;i!=0;i=pre[a[i].u])
                  if (aug>a[i].c)
                    aug=a[i].c;
                maxf+=aug;
                for (i=v;i!=0;i=pre[a[i].u])
                  {
                    a[i].c-=aug;
                    a[i^1].c+=aug;
                  }
                u=vs;
              }
          }
        else
          {
            minl=vt;
            for (i=point[u];i!=0;i=next[i])
              if (a[i].c>0&&minl>lev[a[i].v])
                minl=lev[a[i].v];
            gap[lev[u]]--;
            if (gap[lev[u]]==0) break;
            lev[u]=minl+1;
            cur[u]=point[u];
            gap[lev[u]]++;
            if (u!=vs) u=a[pre[u]].u;
          }
      }
    return maxf;
}
bool work(long long x)
{
    int i,j;
    memset(cur,0,sizeof(cur));
    memset(point,0,sizeof(point));
    memset(next,0,sizeof(next));
    e=1;
    for (i=1;i<=m;++i)
      add(1,i+1,(long long)(bi[i])*x);
    for (i=1;i<=m;++i)
      for (j=1;j<=n;++j)
        if (pic[i][j]==1)
          add(i+1,m+j+1,(long long)(inf));
    for (i=1;i<=n;++i)
      add(m+i+1,m+n+2,(long long)(ai[i])*1000000);
    if (ISAP(1,m+n+2)==sum*1000000)
      return true;
    else return false;
}
int main()
{
    int i,j,minn=2100000000;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
      {
        scanf("%d",&ai[i]);
        sum+=ai[i];
      } 
    for (i=1;i<=m;++i)
      {
        scanf("%d",&bi[i]);
        minn=min(minn,bi[i]);
      }
    for (i=1;i<=m;++i)
      for (j=1;j<=n;++j)
        scanf("%d",&pic[i][j]);
    l=1; r=(long long)(sum/minn)*1000000;
    while (l<r)
      {
        if (work(mid))
          r=mid;
        else
          l=mid+1;
      }
    printf("%0.6f\n",l/(1000000*1.0));
}

T2的暴力分有50分,线性筛法+dfs一遍处理出每个质数的幂,从而处理出每个数的约束个数。但考试的时候考虑的不够周全,就连20分的暴力都写残了。。。正解是部分和+莫比乌斯反演,依旧没听懂。

T3的暴力分有40分,但莫名写残只有20分,正解貌似是线段树+分类讨论,稍稍有点听懂的感觉。。。。

总结:1、考试和平常练习的时候一定要注重几种不同算法的结合,不能只考虑单纯一种算法,省选难度的题不可能只用简单的一种算法或数据结构。

2、写好暴力,在发现有问题时一定要多想一想为什么,要想出治本的方法,不能随便一改过掉样例就跳过。

3、平常多做一些省选难度的题目,逐渐熟悉各种模型转化和建模思想。

4、合理分配做题时间,不要光在一道题上死磕。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值