天天看点

UESTC Summer Training #5 Division I

比赛地址:http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=9380#overview

UESTC Summer Training #5 Division I

今天终于找回了一些感觉了。虽然很遗憾由于多打了相同的一句,导致G没有过,不过与队友的配合终于感觉了。。

开场有些慢,由于我想错了K题,然后就去拍了,打错了一个下标,40分钟后才交了一发错误的K。。。影响了队伍整体的推进速度。

H过后。。在确定K想错后。。开始拍了C(自己在短时间写代码的能力太错了),用了十多分钟才过掉

接着E题,想到了用积分公式爆答案,不过感觉花时间太久了,间隔了35分钟。。。

tc在想D的建树过程,无法证明正确性。最后讨论想到了地推。。这里也出慢了。。打表之类的根据情况选择最有可能的因素打呀。。

A - Association of Strings

不会做

B - Binary Substring

给三个数A,B,C,找最小的X(A<=X<=B)使C的二进制串被包含与X的二进制串

队友写的。。貌似比较暴力。。我想想再更新这里

C - Common Palindrome

给两个串A,B,找一个最长的回文串同时是A,B的子序列

因为串的长度只有60,时限10s,

因此可以用dp[a1][b1][a2][b2]:表示A[a1,b1]与B[a2,b2]能够得到最长回文

那么枚举[a1,b1]配[a2,b2]的最长回文首尾是哪两个相同字母

则dp[a1][b1][a2][b2]={2+dp[][][][]}(里面的变换应该不用我说吧)

还可能就是只包含一个字母、、、注意下各种限制条件。。这题就很好写了。

D - Draw and Score

N个点构成一个二叉树,每次连接一个点进去,一共进行N-1次,每次统计左右儿子数相同的节点数Ci,最终求Ci和的最大值

递推+打表找规律。

F[n]:n个点能够达到的最长值

F[n]=max{F[i]+F[n-i-1]+min(i,n-i-1)}(  0<=i<=n-i-1 )

E - Elliptic Athletics Track

给a,b(a,b<=20),求椭圆的周长

祖冲之求圆周长的方法。对 x,x+dx,求出对应的y,从而得到dy,将sqrt(dx^2+dy^2)作为在那一小段区间内,椭圆的的弧长

F - Fun with Binary Tree

一颗二叉树,一个命令字符串包含“L”,“R”,分别表示走到当前点的左右儿子,你可以不执行一个命令,但不能连续两次拒绝。问是否存在完成所有命令的方案。

最暴力的方法:对每个指令,记录当前可以到达哪些点,一直执行到最后一个指令,看是否有满足的点,这种方法要T,有一个很好的剪枝:对一个节点,如果往下可以连续同样的操作(全为L或者R)走L步,刚好当前的指令从这个点也可以连续做相同的操作L步,那么我们不必一步一步的往下走,通过分析,可以直接得到某些点能够到达某些指令。。(减少了大概L/2的样子)。。大致思路就这样我还没写。。

G - Good Measures of Dispersion

N个数,每次可以对区间执行3个操作:置为一个数,加一个数,求这个区间的方差,和最值的差值

很裸一道线段树的题。

方差的公式=(N*平方和 - 和的平方)/(N*N)  (N表示数目)

某一区间增加a,平方和=(Xi+a)^2 = Xi^2 +2*Xi*a+a*a

=原来的平方和+2*a*和+N*a^2

注意下懒操作之类的就行了。附上我的代码,代码长度很短^_^

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int const N=100005;
long long oo=(1LL<<62)-1LL;
int num[N],Node;
struct node
{
    int a,b,l,r;
    long long sum,ssum,Min,Max,add,setN;
    bool flag;
    void read(int a,int b){  this->a=a; this->b=b;flag=false;add=0;}
    void Add(long long c)
    {
        Min+=c;Max+=c;
        ssum+=sum*c*2LL+c*c*(b-a+1);
        sum+=c*(b-a+1);
        if(flag)setN+=c;
        else add+=c;
    }
    void Set(long long c)
    {
        flag=true;
        sum=ssum=setN=Min=Max=add=0;
        Add(c);
    }
}tr[N*2],tmp;
void update(int k,int l,int r)
{
   tmp=tr[k];
   tmp.Max=max(tr[l].Max,tr[r].Max);
   tmp.Min=min(tr[l].Min,tr[r].Min);
   tmp.sum=tr[l].sum+tr[r].sum;
   tmp.ssum=tr[l].ssum+tr[r].ssum;
   tr[k]=tmp;
}
void makeTree(int a,int b)
{
   int k=++Node;
   tr[k].read(a,b);
   if(a==b){ tr[k].Set(num[a]); return; }
   tr[k].l=Node+1; makeTree(a,(a+b)/2);
   tr[k].r=Node+1; makeTree((a+b)/2+1,b);
   update(k,tr[k].l,tr[k].r);
}
void insert(int a,int b,long long c,int cmd,int k)
{
   if(tr[k].b<=b&&tr[k].a>=a)
   {
      switch(cmd)
      {
         case 0:tr[k].Set(c);break;
         case 1:tr[k].Add(c);break;
         case 2:update(0,0,k);break;
      }
   }
   else
   {
        if(tr[k].flag)
        {
            tr[tr[k].l].Set(tr[k].setN);
            tr[tr[k].r].Set(tr[k].setN);
            tr[k].flag=false;
        }
        if(tr[k].add)
        {
            tr[tr[k].l].Add(tr[k].add);
            tr[tr[k].r].Add(tr[k].add);
            tr[k].add=0;
        }
        int m=(tr[k].a+tr[k].b)>>1;
        if(a<=m)insert(a,b,c,cmd,tr[k].l);
        if(b> m)insert(a,b,c,cmd,tr[k].r);
        update(k,tr[k].l,tr[k].r);
   }
}
long long gcd(long long a,long long b)
{
   if(b==0)return a;
   return gcd(b,a%b);
}
int main()
{
 
  int T,n,m;
  long long t,x,y,d;
  scanf("%d",&T);
  for(int cas=1;cas<=T;cas++)
  {
      printf("Case %d:\n",cas);
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)scanf("%d",&num[i]);
      Node=0;makeTree(1,n);
      int cmd,a,b,c;
      for(int i=1;i<=m;i++)
      {
           scanf("%d",&cmd);
           if(cmd==0||cmd==1)
           {
                scanf("%d%d%d",&a,&b,&c);
                insert(a,b,c,cmd,1);
           }
           else
           {
               scanf("%d%d",&a,&b);
               tr[0].Min=oo;tr[0].Max=-oo;
               tr[0].sum=0;tr[0].ssum=0;
               insert(a,b,0,cmd,1);
               t=b-a+1;
               x=tr[0].ssum*t-tr[0].sum*tr[0].sum;
               y=t*t;
               d=gcd(x,y);
               printf("%lld/%lld ",x/d,y/d);
               printf("%lld\n",tr[0].Max-tr[0].Min);
            }
      }
  }
  return 0;
}
           
UESTC Summer Training #5 Division I

H - Hardest Problem Ever (Easy)

签到题

I - Interesting Route

没读

J - Just Some Permutations

没读

K - K-Neutral Rectangles

N*M的矩阵,求一个最大面积的矩阵,使得所选矩阵内任意相邻两点的数字差的绝对值不超过K。

求矩阵的问题,一般都要枚举行,表示最大面积的矩阵的最下面那条边在这行上。

这题也一样。。枚举每行,然后在这上面求一个最大矩阵

如果最优矩阵只含一列的话,可以维护H[j],表示从第i行往上走,该列最远能走的距离。然后考虑多列的情况,用S[j]表示第j列与第j+1列相连,能往上走的最远距离,很明显S[j]<=H[j]

4
1 3 4
1 2 3 4
1 2 3 4

 如图:H[1]=3,H[2]=2,H[3]=3,H[4]=4

你会发现这个问题就转化成通过S[j]求最大的矩阵: 比如我们取第1,2列,得到S=min(S[1],S[2])=2,那么高度就为2,长是多少呢?

是2么? 显然不对,因为S[j]表示与第j+1列能够匹配的最大高度,显然第3列有两个元素与第2列能够相邻(s[2]=2)

因此这样得到一个2*3的矩阵,大家是否已经想到做法了?与广告印刷这题就几乎一模一样了,求得L[j],R[j]表示向左,向右最远走到的列的编号。唯一不同的是最后算面积是:s[j]*(R[j]-L[j]+1+1 )

附广告印刷的单调队列的处理方式(今天自己才想到的一个写法)

H[N],L[N],R[N]// 每列的高度,向左,向右能到达的位置

Q[N],tp;// 维护的单调上升的队列,Q[i]记录的位置编号

ps:以前写这种我都是从左扫一次,从右扫了一次,其实一次扫描就行了

void  Ins(int k)

{

     L[k]=R[k]=k;// 初始第k列左右能够到达的位置

    while(tp!=0&&H[Q[tp]]>=H[k])// 队列最后那列的高度不小于当前高度

    {

        L[k]=L[Q[tp]];  //更新第k列左边能到的位置

        R[Q[tp-1]]=R[Q[tp]];  // 更新队列倒数第二那列右边能够达到的位置

        tp--;

    }

    ++tp;Q[tp]=k;

}

for(int k=1;k<=m;k++)Ins(k);

Ins(0);// 对最后形成的单调队列处理,让所有的出队列