2023“钉耙编程”中国大学生算法设计超级联赛(5)

Typhoon 计算几何,点到线段距离

String Magic (Easy Version) Manacher可持久化线段树

Touhou Red Red Blue DP 模拟

Expectation (Easy Version)    签到,组合数学

Tree 树形DP

Cactus Circuit 仙人掌图,tarjan找简单环

Counting Stars 暴力,组合数学

直接套计算几何模板,求点到线段最短距离即可 

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
# define mod 1000000007
double feng[10000+10][2],wo[10000+10][2];
class Point
{

public:
    double x,y;

    Point(double x=0,double y=0):x(x),y(y) {}

    //向量加法
    Point operator+(Point p)
    {
        return Point(x+p.x,y+p.y);
    }
    //向量减法
    Point operator-(Point p)
    {
        return Point(x-p.x,y+p.y);
    }
    //向量伸缩
    Point operator*(double a)
    {
        return Point(x*a,y*a);
    }
    Point operator/(double a)
    {
        return Point(x/a,y/a);
    }
    //向量大小
    double abs()
    {
        return sqrt(norm());
    }
    //向量范数
    double norm()
    {
        return x*x+y*y;
    }

    bool operator<(const Point &p) const
    {
        return x!=p.x?x<p.x:y<p.y;
    }

    bool operator==(const Point &p)const
    {
        return x-p.x<1e-10&&y-p.y<1e-10;
    }

};

typedef Point Vector;

//向量内积
double dot(Vector a,Vector b)
{
    return a.x*b.x+a.y*b.y;
}

//向量外积
double cross(Vector a,Vector b)
{
    return abs(a.x*b.y-a.y*b.x);
}

//正交
bool isOrthogonal(Vector a,Vector b)
{
    return a.x*b.x+a.y*b.y==0;
}
//平行
bool isParallel(Vector a,Vector b)
{
    return a.x*b.y-a.y*b.x==0;
}

//投影
Point project(Point a,Point b,Point c)
{
    Vector ab(b.x-a.x,b.y-a.y);
    Vector ac(c.x-a.x,c.y-a.y);
    double  r=dot(ab,ac)/ab.norm();//比例
    Vector h(ab*r);
    return Point(a.x+h.x,a.y+h.y);
}

//映象

Point reflect(Point a,Point b,Point c)
{
    //c到ab的投影点
    Point r=project(a,b,c);
    Vector cr(r.x-c.x,r.y-c.y);
    //cr扩大二倍
    Vector cr_2=cr*2;//上面重载过*
    //向量加法
    return Point(c.x+cr_2.x,c.y+cr_2.y);
}

//两点间距离
double getDistancePP(Point a,Point b)
{
    Point c(b.x-a.x,b.y-a.y);
    return  c.abs();
}

//点到直线距离(利用外积平行四边形)
double getDistancePL(Point a,Point b,Point c)
{
    Vector ab(b.x-a.x,b.y-a.y);
    Vector ac(c.x-a.x,c.y-a.y);

    return cross(ab,ac)/ab.abs();
}

//点到线段距离
double getDistancePS(Point a,Point b,Point c)
{
    //定义4个向量
    Vector ab(b.x-a.x,b.y-a.y);
    Vector ba(a.x-b.x,a.y-b.y);
    Vector ac(c.x-a.x,c.y-a.y);
    Vector bc(c.x-b.x,c.y-b.y);

    if(dot(ab,ac)<0.0) return getDistancePP(a,c);
    if(dot(ba,bc)<0.0) return getDistancePP(b,c);

    return getDistancePL(a,b,c);
}


//线段到线段的距离
double getDistanceSS(Point a,Point b,Point c,Point d)
{
    //从4个点到2线段距离中取最小
    return min(min(getDistancePS(c,d,a),getDistancePS(c,d,b)),min(getDistancePS(a,b,c),getDistancePS(a,b,d)));
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);

    for(int i=1; i<=n; i++)
    {
        scanf("%lf%lf",&feng[i][0],&feng[i][1]);
    }

    for(int i=1; i<=m; i++)
    {
        scanf("%lf%lf",&wo[i][0],&wo[i][1]);
    }

    for(int i=1; i<=m; i++)
    {
        double minn=1e18;
        for(int j=2; j<=n; j++)
        {
            struct Point A,B,C;
            A.x=wo[i][0];
            A.y=wo[i][1];
            B.x=feng[j-1][0];
            B.y=feng[j-1][1];
            C.x=feng[j][0];
            C.y=feng[j][1];
            double temp=getDistancePS(B,C,A);
            //  cout<<temp<<" ";
            minn=min(minn,temp);
        }

        // cout<<endl;

        cout<<fixed<<setprecision(4)<<minn<<'\n';
    }

    return 0;
}

 

按照给出的约束,可以推出,这是一个偶数长度的字符串,左半部分是回文,右半部分也是回文,并且整体也是回文。也就是说,我们需要找出全部的偶数回文串,它的左半部分也是回文串。按照回文的奇偶性来判断。以左半部分是偶数回文为例,假设i位置到i+1位置的最大回文半径为图中长方形。如果回文半径为偶数,则左半部分的回文中心应该在中间位置,到i-1位置之间。只有这样,对称出来的字符串才能覆盖i,且保证左半部分的内部回文左右相等。比如偏向更左侧,会导致左右不均,超出左半部分的左边界。

而对于奇数回文,也是同理,不再赘述。

维护采用可持久化线段树,每次将回文中心所在线段树的右端点加1,查询时,取合法回文半径区间,求出其大于等于i位置的和即可。

 

#include <bits/stdc++.h>
using namespace std ;
typedef long long int ll;
int P[200000+10];
string s;
int d[200000+10][2];
int lson[200000*30+10],rson[200000*30+10],sum[200000*30+10];
int tot;
int clone(int root)
{
    tot++;
    lson[tot]=lson[root];
    rson[tot]=rson[root];
    sum[tot]=sum[root];
    return tot;
}
int build(int root,int l,int r)
{
    root=clone(root);
    if(l==r)
    {
        sum[root]=0;
        return root;
    }
    int mid=(l+r)>>1;
    lson[root]=build(lson[root],l,mid);
    rson[root]=build(rson[root],mid+1,r);
    sum[root]=0;
    return root;
}
int change(int root,int l,int r,int pos)
{
    root=clone(root);
    if(l==r)
    {
        sum[root]++;
        return root;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
        lson[root]=change(lson[root],l,mid,pos);
    else
        rson[root]=change(rson[root],mid+1,r,pos);
    sum[root]=sum[lson[root]]+sum[rson[root]];
    return root;
}
int getsum(int root1,int root2,int l,int r,int pos)
{
    if(pos<=l)
        return sum[root2]-sum[root1];
    int mid=(l+r)>>1;
    int ans=0;
    if(pos<=mid)
    {
        ans+=getsum(lson[root1],lson[root2],l,mid,pos);
        ans+=getsum(rson[root1],rson[root2],mid+1,r,pos);
        return ans;
    }
    else
    {
        ans+=getsum(rson[root1],rson[root2],mid+1,r,pos);
    }
    return ans;
}
int root[200000+10][2];
signed main()
{

    cin.tie(0);
    ios::sync_with_stdio(0);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>s;
        string temp="";
        temp=" ";
        int mid=0,r=0;
        for(int i=0; i<s.length(); i++)
        {
            temp+='#';
            temp+=s[i];
        }
        temp+='#';
        temp+='@';
        for(int i=1; i<temp.length(); i++)
        {
            if(r<i)
                P[i]=1;
            else
                P[i]=min(P[2*mid-i],r-i);
            while(temp[i-P[i]]==temp[i+P[i]])
                P[i]++;
            if(mid+P[i]>r)
            {
                r=mid+P[i];
                mid=i;
            }
        }
        for(int i=1; i<temp.length(); i++)
        {
            if(i%2==0)
            {
                d[i/2][1]=P[i]/2;
            }
            else if(i>=3)
            {
                d[i/2][0]=P[i]/2;
            }
        }
        int n=s.length();
        tot=0;
        root[0][0]=build(0,1,n);
        root[0][1]=build(0,1,n);
        ll ans=0;
        for(int i=1; i<n; i++)
        {
            root[i][1]=change(root[i-1][1],1,n,i+d[i][1]-1);
            if(d[i][0])
            {
                int pre=0;
                if(d[i][0]%2==0)
                    pre=i-d[i][0]/2;
                if(d[i][0]%2==1)
                    pre=i-d[i][0]/2-1;
                ans+=getsum(root[pre][1],root[i][1],1,n,i);
            }
            if(i!=n)
                root[i][0]=change(root[i-1][0],1,n,i+d[i][0]);
            if(d[i][0]>1)
            {

                int pre=0;
                if(d[i][0]%2==0)
                    pre=i-d[i][0]/2-1;
                if(d[i][0]%2==1)
                    pre=i-d[i][0]/2-1;
                ans+=getsum(root[pre][0],root[i-1][0],1,n,i);
            }
        }
        cout<<ans<<'\n';
    }
    return 0;
}

 设dp[i][j]为第一个是i第二个是j的状态时的最大收益。其中i,j=0时代表空,值得注意的是,不能直接用dp数组推dp数组,需要暂时存储一下之前状态,再进行转移。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
int dp[5][5], cp[5][5];
bool ext[5][5], cext[5][5];
int q[N];
char s[N];
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        memset(dp, 0, sizeof dp);
        memset(ext, 0, sizeof ext);
        scanf("%s", s);
        int n = strlen(s);
        for (int i = 1; i <= n; i++)
        {
            if (s[i - 1] == 'R')
            {
                q[i] = 1;
            }
            else if (s[i - 1] == 'G')
            {
                q[i] = 2;
            }
            else
            {
                q[i] = 3;
            }
        }
        for(int i=0; i<=3; i++)
        {
            for(int j=0; j<=3; j++)
            {
                dp[i][j]=-1e9;
            }
        }
        dp[0][0]=0;
        for (int i = 1; i <= n; i++)
        {
            memcpy(cp, dp, sizeof cp);


            dp[1][0] = max(dp[1][0], cp[q[i]][q[i]] + 1);
            dp[2][0] = max(dp[2][0], cp[q[i]][q[i]] + 1);
            dp[3][0] = max(dp[3][0], cp[q[i]][q[i]] + 1);
         //   ext[1][0] = ext[2][0] = ext[3][0] = 1;


            for (int j = 1; j <= 3; j++)
            {
                dp[j][q[i]] = max(dp[j][q[i]], cp[j][0]);
            }
            for (int j = 1; j <= 3; j++)
            {
                for (int k = 1; k <= 3; k++)
                {
                    if (k != j && k != q[i] && j != q[i])
                    {
                        for (int u = 1; u <= 3; u++)
                        {
                            for (int v = 1; v <= 3; v++)
                            {
                                dp[u][v] = max(dp[u][v], cp[j][k]);
                                ext[u][v] = 1;
                            }
                        }
                    }
                }
            }
            dp[q[i]][0]=max(dp[q[i]][0],cp[0][0]);
            for (int u = 1; u <= 3; u++)
            {
                for (int v = 1; v <= 3; v++)
                {
                   dp[v][q[i]] = max(dp[v][q[i]], cp[u][v]);
                }
            }
        }
        int res = 0;
        for (int i = 0; i <= 3; i++)
        {
            for (int j = 0; j <= 3; j++)
            {
                res = max(res, dp[i][j]);
            }
        }
        printf("%d\n", res);
    }
}

签到题, 快速幂解决。

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
# define mod 998244353
ll fac[1000000+10],inv[1000000+10];
ll qp(ll base, ll pow)
{
    ll ans=1;
    base%=mod;
    while(pow)
    {
        if(pow&1)
            ans=ans*base%mod;
        base=base*base%mod;
        pow>>=1;
    }
    return ans;
}
ll sum[1000000+10];
void init()
{
    fac[0]=1;
    for(int i=1; i<=1000000; i++)
    {
        fac[i]=fac[i-1]*(ll)i%mod;
    }
    inv[1000000]=qp(fac[1000000],mod-2);

    for(int i=1000000-1;i>=0;i--)
    {
        inv[i]=inv[i+1]*(ll)(i+1)%mod;
    }
}
ll getc(int x,int y)
{
    if(x<y)
        return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
ll sheng[1000000+10],bai[1000000+10];
int main()
{

    int t;
    init();
  //  cout<<fac[100]*inv[100]%mod;
    cin>>t;

    while(t--)
    {
        ll n,m,a,b;
        scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
        ll aa=a*qp(b,mod-2)%mod;

        ll bb=((1-aa)%mod+mod)%mod;

        sheng[1]=aa;
        bai[1]=bb;
        sum[1]=qp(1ll,m);

        sheng[0]=1;
        bai[0]=1;
        for(int i=1; i<=n; i++)
        {
            sum[i]=qp(i,m);
            sum[i]=(sum[i-1]+sum[i])%mod;
            sheng[i]=sheng[i-1]*aa%mod;
            bai[i]=bai[i-1]*bb%mod;

        }
        ll ans=0;

        for(int i=1; i<=n; i++)
        {
            ll now=getc(n,i)*sheng[i]%mod*bai[n-i]%mod;
            now*=sum[i];
            now%=mod;
            ans+=now;
            ans%=mod;

        }
        cout<<ans<<'\n';
    }

    return 0;
}

 

很抽象的题意,说的一点不清楚。大致是每一个根节点要么是重儿子,要么不是。是的话,会形成一条重儿子链,这条重儿子链的顶端的父节点,将这条重儿子链形成的线段树叠在自己头顶,父节点其余的轻儿子,坠在父节点下面。问给定树形成的新树高度。

如果是重儿子,每次返回两个值,一个是当前其接龙的重链长度,一个是该节点下面坠的树高。

如果不是重儿子,则返回两个值,一个是将其重链接在头顶和其下端坠着的树高之和,一个是在接龙的重链长度,此时为0.

记录一个up,只对轻儿子有效, 表示接在其头部的长度,初始必须是1,因为单纯一个数字也是一层。

记录一个down,代表当下面连接的高度,对全部儿子节点取max即可。

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int N = 1e6 + 7;
int h[N], e[N], ne[N], idx;
int v[N];
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
ll res;
typedef pair<int, int> pii;
int maxx(int &a, int &b)
{
    if (a > b)
    {
        return a;
    }
    return b;
}
inline pii dfs(int x)
{
    int up = 1;
    int down = 0;
    int len = v[x];
    int flag=0;
    for (register int i = h[x]; i != -1; i = ne[i])
    {
        int to = e[i];
        if (v[to])
        {
            pii tp = dfs(to);
            up = ceil(log2((tp.second + 1) * 2));
            down = maxx(down, tp.first);
            len += tp.second;
            flag=tp.second+1;
        }
        else
        {
            pii tp = dfs(to);
            down = maxx(down, tp.first);
        }
    }
    if(v[x]==0)
        len=0;
    pii tpp0 = {up + down, len}, tpp1 = {down, len};
    return (v[x] == 0) ? tpp0 : tpp1;
}
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

inline void write(ll x)
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
int main()
{
    int size(512 << 20);
    // 512M
    __asm__("movq %0, %%rsp\n" ::"r"((char *)malloc(size) + size)); // YOUR CODE
    ios::sync_with_stdio(false);
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        idx = 0;
        int n;
        cin >> n;
        for (register int i = 0; i <= n; i++)
        {
            v[i] = 0;
            h[i] = -1;
        }
        for (register int i = 1; i <= n; i++)
        {
            register int x;
            cin >> x;
            add(x, i);
        }
        for (register int i = 1; i <= n; i++)
        {
            register int x;
            cin >> x;
            if (x)
            {
                v[x] = 1;
            }
        }
        cout << dfs(1).first << '\n';
    }
    exit(0);
}

每条边都有一个开始工作的时间,同时也有一个工作持续的时间,不工作的时候,这条边不联通。问图整体联通的最长时间(可以任意安排每个边的起始工作时间)

图是仙人掌,也就是若干简单环的连接。对于环外的桥,必须从0时刻就开始工作,而一旦这样,就决定了要对所有桥边取min,作为答案的初始值。

对于每一个简单环内部,只需要让其len-1条边工作即可。也就是说,两条边的环,工作时间为w1+w2,三条边的w1<=w2<=w3时,为min(w1+w2,w3)意思就是,先让w1和其他除了w2之外的,工作,再让w2和其他除了w1的工作,如果w3>w1+w2,则完全可以,否则就为w3.于是题目转化为求出仙人掌图的每一个简单环的每一条边。

#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;

struct edge
{
    int u,v,w,id;
} o[N<<1];

vector<pair<int,int> > e[N];

int dfn[N],low[N],tot;

stack<edge> st;

int ans=2e9;

void tarjan(int x,int f)
{
    dfn[x]=low[x]=++tot;
    for (auto [p,id]:e[x]) if (!dfn[p])
        {
            st.push(o[id]);
            tarjan(p,id);
            low[x]=min(low[x],low[p]);
            if (low[p]==dfn[x])
            {
                edge nw;
                vector<int> g;
                do
                {
                    nw=st.top();
                    st.pop();
                    g.push_back(nw.w);
                }
                while (nw.id!=id);
                sort(g.begin(),g.end());
                if (g.size()==1) ans=min(ans,g[0]);
                else if (g.size()==2) ans=min(ans,g[0]+g[1]);
                else ans=min(ans,min(g[0]+g[1],g[2]));
            }
        }
        else
        {
            if (dfn[x]>dfn[p] && id!=f) st.push(o[id]);
            low[x]=min(low[x],dfn[p]);
        }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T;
    cin>>T;
    while (T--)
    {
        int n,m,i;
        cin>>n>>m;
        tot=0;
        ans=2e9;
        for (i=1; i<=n; i++) e[i].clear(),dfn[i]=low[i]=0;
        for (i=1; i<=m; i++)
        {
            cin>>o[i].u>>o[i].v>>o[i].w;
            o[i].id=i;
            e[o[i].u].push_back({o[i].v,i});
            e[o[i].v].push_back({o[i].u,i});
        }
        tarjan(1,0);
        cout<<ans<<endl;
    }
}

直接模拟,因为度数之和是1e6级别,故直接模拟,但卡常 

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
# define mod 1000000007
ll fac[1000000+10],inv[1000000+10];
ll qp(ll base, ll pow)
{
    ll ans=1;
    base%=mod;
    while(pow)
    {
        if(pow&1)
            ans=ans*base%mod;
        base=base*base%mod;
        pow>>=1;
    }
    return ans;
}
ll sum[1000000+10];
void init()
{
    fac[0]=1;
    for(int i=1; i<=1000000; i++)
    {
        fac[i]=fac[i-1]*(ll)i%mod;
    }
    inv[1000000]=qp(fac[1000000],mod-2);

    for(int i=1000000-1; i>=0; i--)
    {
        inv[i]=inv[i+1]*(ll)(i+1)%mod;
    }
}
ll getc(int x,int y)
{
    if(x<y)
        return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int du[1000000+10];
ll ans[1000000+10];
unordered_map<int,int>mp;
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

inline void write(ll x)
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
int main()
{

    int t;
    cin>>t;
    init();
    while(t--)
    {
        int n,m;
        n=read();
        m=read();
        for(int i=1; i<=n; i++)
        {
            du[i]=0;
            ans[i]=0;
        }
        mp.clear();
        for(int i=1; i<=m; i++)
        {
            int x,y;
            x=read();
            y=read();
            du[x]++;
            du[y]++;
        }
        for(int i=1; i<=n; i++)
        {
            mp[du[i]]++;
        }
        for(auto it:mp)
        {
            for(int j=2; j<=it.first; j++)
            {
                ll temp=(ll)getc(it.first,j)*(ll)it.second%mod;
                ans[j]+=temp;
                ans[j]%=mod;
            }
        }
        ll fuck=0;
        for(int i=2; i<n; i++)
        {
            fuck^=ans[i];
        }
        cout<<fuck<<'\n';
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦三码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值