The 2019 ICPC Asia Shanghai Regional Contest 补题

  重现赛过了三道题,我做出来了F题,其他题面都看了看没想到思路~

  挨个看题意看到了F,题意不就n个点的树,每个点有初始点权,需要维护m次操作:操作1:u到v的路径上的点权变成w。2:u到v的路径上的点权加w。3:u到v的路径上的点权乘w。4:求u到v的路径上的点权立方和。树上路径我会lca和树剖,要做到这样程度的修改用树剖+线段树比较合适。那么问题变成了维护区间立方和。我之前见过维护区间平方和的题,非常厉害,只需要维护区间和与区间平方和,每次pushdown的时候手推一下式子即可。我把三次方的式子(x+a)^3展开之后发现也很可写。那就开始码呗,想当年我可是连主席树都会写的人。码了半年才码出来,边的结构体还开小了,越界了好多发。

  A数多米诺骨牌的数量。RiCi太大了但是我会离散化。一个n^2的做法是预处理出每个点向右向下延伸的最长长度,然后先枚举点再枚举骨牌的大小,O(1)的判断能否组成。写着写着发现O(1)查询有点难搞,最后用了map多了个log(捂脸),不出意外的在第一组数据T飞了。

#include<bits/stdc++.h> 
using namespace std;
const int N=5000010;
int n;
struct node
{
    int x,y;
    friend bool operator < (node a,node b)
    {
        return a.x==b.x?a.y<b.y:a.x<b.x;
    }
}o[N];
map<node,int>maxx,maxy;
bool cmpx(node a,node b)
{
    return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmpy(node a,node b)
{
    return a.y==b.y?a.x<b.x:a.y<b.y;
}
int main()
{
    int T,k,n;scanf("%d",&T);
    for(k=1;k<=T;k++)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&o[i].x,&o[i].y);
        maxx.clear();
        maxy.clear();
        sort(o+1,o+1+n,cmpx);
        for(int i=n;i;i--)
            if(o[i].x==o[i+1].x&&o[i].y==o[i+1].y-1)
                maxy[node{o[i].x,o[i].y}]=maxy[node{o[i+1].x,o[i+1].y}]+1;
            else
                maxy[node{o[i].x,o[i].y}]=1;
        sort(o+1,o+1+n,cmpy);
        for(int i=n;i;i--)
            if(o[i].y==o[i+1].y&&o[i].x==o[i+1].x-1)
                maxx[node{o[i].x,o[i].y}]=maxx[node{o[i+1].x,o[i+1].y}]+1;
            else
                maxx[node{o[i].x,o[i].y}]=1;
        int ans=0;
        for(int i=1;i<=n;i++)//1*2 
        {
            int a=maxx[node{o[i].x,o[i].y}],b=maxy[node{o[i].x,o[i].y}];
            for(int xx=1,yy=2;xx<=a&&yy<=b;xx++,yy+=2)
                if(maxy[node{o[i].x+xx-1,o[i].y}]>=yy&&maxx[node{o[i].x,o[i].y+yy-1}]>=xx)
                    ans++;
            for(int xx=2,yy=1;xx<=a&&yy<=b;xx+=2,yy++)
                if(maxy[node{o[i].x+xx-1,o[i].y}]>=yy&&maxx[node{o[i].x,o[i].y+yy-1}]>=xx)
                    ans++;
        }
        printf("Case #%d: %d
",k,ans); 
        
    }
}
A题Tn^2logn复杂度起飞代码

   写完了O(1)查询的代码,竟然A掉了?

#include<bits/stdc++.h> 
using namespace std;
const int N=5000010;
int n;
struct node
{
    int x,y,i
}o[N];
int maxx[N],maxy[N],xi[N],ix[N],yi[N],iy[N];
bool cmpx(node a,node b)
{
    return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmpy(node a,node b)
{
    return a.y==b.y?a.x<b.x:a.y<b.y;
}
bool cmpi(node a,node b)
{
    return a.i<b.i;
}
int main()
{
    int T,k,n;scanf("%d",&T);
    for(k=1;k<=T;k++)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&o[i].x,&o[i].y),o[i].i=i;
        o[n+1].x=0;
        o[n+1].y=0;
        o[n+1].i=0;
        sort(o+1,o+1+n,cmpx);
        for(int i=n;i;i--)
        {
            xi[i]=o[i].i;
            ix[o[i].i]=i;
            if(o[i].x==o[i+1].x&&o[i].y==o[i+1].y-1)
                maxy[o[i].i]=maxy[o[i+1].i]+1;
            else
                maxy[o[i].i]=1;
        }
        sort(o+1,o+1+n,cmpy);
        for(int i=n;i;i--)
        {
            yi[i]=o[i].i;
            iy[o[i].i]=i;
            if(o[i].y==o[i+1].y&&o[i].x==o[i+1].x-1)
                maxx[o[i].i]=maxx[o[i+1].i]+1;
            else
                maxx[o[i].i]=1;
        }
        sort(o+1,o+1+n,cmpi);
        int ans=0;
        for(int i=1;i<=n;i++)//1*2 
        {
            int a=maxx[i],b=maxy[i];
            for(int xx=1,yy=2;xx<=a&&yy<=b;xx++,yy+=2)
                if(maxy[yi[iy[i]+xx-1]]>=yy&&maxx[xi[ix[i]+yy-1]]>=xx)
                    ans++;
            for(int xx=2,yy=1;xx<=a&&yy<=b;xx+=2,yy++)
                if(maxy[yi[iy[i]+xx-1]]>=yy&&maxx[xi[ix[i]+yy-1]]>=xx)
                    ans++;
        }
        printf("Case #%d: %d
",k,ans); 
        
    }
}
总之就各种映射

   B题队友A掉了,看了题意,好想好写。问的是一堆字符串有没有是别人的前缀的。考虑读进来,sort之后用map维护即可。

#include<bits/stdc++.h>
using namespace std;
map<string,int>mp;
string s[10010],t;
int len,n,T,i;
int main()
{
    cin>>T;
    for(int k=1;k<=T;k++)
    {
        cin>>n;
        mp.clear();
        for(i=1;i<=n;i++)
            cin>>s[i];
        sort(s+1,s+1+n);
        for(i=1;i<=n;i++)
        {
            len=s[i].length();
            t.clear();
            for(int j=0;j<len;j++)
            {
                t+=s[i][j];
                if(mp[t])
                {
                    j=len+2;
                    i=n+1;
                    break;
                }
            }
            mp[s[i]]=1;
        }
        printf("Case #%d: ",k);
        if(i==n+2)
            printf("No
");
        else
            printf("Yes
");
    }
}
B

   C就是在一个N*M矩形内左下角和右上角不能走,同时给定S个有财宝的点,K个有障碍的点不能走。问从左上角到右下角的方案数。我一看S才10,想到了容斥+二维DP。但是看到N、M<=100000就自闭了,DP不太行,容斥依然还行。注意到每一列的dp方程可以写矩阵乘法,但那是n^2的,不行。 想到了容斥之后问题就变成了M*N的矩阵左下角大小为a右上角大小为b的角落不能走的方案数,我和yxh想了很久也没想到,溜了溜了。

  E题是在一个有点权的矩形中规定起点和终点,从一个点走到另一个点时如果下一个点没走过就可以使答案+两个点权的乘积。

  

原文地址:https://www.cnblogs.com/qywyt/p/14684745.html