2022杭电多校1(总结+补题)

目录

总结:

题解:

1001 - String

1003 - Backpack

1009 - Laser

总结:

       今天开场2分钟刚确定两道签到题1011和1012忽然肚子疼,拿着手机冲去厕所并让队友把这两题翻译发出来,在蹲坑时想出了结论,恰好此时队友也想出来了。回来之后队友已经把第一道签到出了,另一个队友也把第二道签到思路想出来了,我就继续去开其他题,签完道之后是长达两个小时的坐牢,我和队友同时在开1003和1002,想了一个多小时只确定了一个稍微超一点复杂度的暴力,另外队友一直在写1002的搜索,两小时出头时我写了一发1003的暴力,不出意料的T了,放弃1003去看过题数正在增长的1009,过了一会队友告诉我1003用shuffle+卡时过了,遂队友1继续看1002搜索,我和另外一个队友开1009的计算几何,讨论了大概十五分钟,我就和另外一个队友确定了大致思路,并开始画图模拟并开始写代码,写好代码之后debug了几分钟后一发直接过。然后我就去看1001,留下两个队友对着他们1002的搜索代码debug,到最后我1001依旧没看懂题目,最后五分钟,队友找到了另外一个队友写的搜索判断边界的bug,在04:59:16交了第十四发,过了1002,五题rk164结束比赛。

题解:

1001 - String

题意:

S_{len}=[S_1,...,S_{len}] ,定义一个F_G,F_G的值为满足以下条件的正整数 x 的个数:

1. 1\leqslant x\leqslant G_{len}

2. G[1,...,x]=G[G_{len}-x+1,...G_{len}] (即G的前x元素与后x个元素组成的串相同,后面称这两个串为左串和右串)。

3. 左串和右串的公共部分长度(相交部分的长度)大于0且该长度len%k=0

问所有 (i\in 1-n) 的 (F_{S[1,...i]}+1) 相乘的结果对998244352取模的结果是多少,即问

{\textstyle \prod_{i=1}^{n}} (F_{S[1,...i]}+1)\mod 998244353

样例解释:

做法:

先用Z函数(扩展KMP)求出每个i开始的后缀的最长公共前缀(LCP),再用模意义下的差分数组,从i=1\sim len中对从 (i-1)*2+k 到 i+z[i-1]-1 (z函数的z数组从0\sim n-1)中的所有满足x%k=((i-1)*2+k)%k 的 x 都加上1,表示从当前位置开始的LCP对x的贡献。最后再对每个模意义下的差分数组做前缀和得到预处理后的每个F值,将所有 (F+1) 的值相乘并对998244353取模得到答案。(注意差分数组要稍微开大一丢丢)

(i-1)*2+k 开始是因为z数组表示从i开始的后缀的最长公共前缀,第一个i-1 表示公共部分的前i-1个字符,第二个i-1表示公共部分的后i-1个字符,这 (i-1)*2 个字符是不属于公共部分的长度,因此我们要从非公共部分长度每次+k开始枚举到i后缀最长能到达的值i+z[i-1]-1 ,表示从当前字符i开始能给这些字符贡献1个x,给这些值都加上1。

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
vector<int> z_function(const string& ss)
{
    int len=(int)ss.length();
    vector<int> z(len,0);
    for(int i=1,l=0,r=0;i<len;i++)
    {
        if(i<=r)
            z[i]=min(r-i+1,z[i-l]);
        while(i+z[i]<len&&ss[z[i]]==ss[i+z[i]])
            z[i]++;
        if(i+z[i]-1>r)
        {
            l=i;
            r=i+z[i]-1;
        }
    }
    return z;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        string ss;
        cin>>ss;
        cin>>k;
        int len=ss.length();
        auto z=z_function(ss);
        z[0]=len;
        vector<vector<int>> a(k,vector<int>(len/k+3,0));
        function<void(int,int)> change = [&](int l,int r)
        {
            if(l>r)
                return;
            int idx=l%k;
            int l1=(l+k-1)/k;
            int r1=l1+(r-l)/k;
            a[idx][l1]+=1;
            a[idx][r1+1]-=1;
        };
        vector<int> num(k,0);
        for(int i=1;i<=len;i++)
            num[i%k]++;
        for(int i=1;i<=len/2+1;i++)
        {
            int l=(i-1)*2+k;
            int r=i+z[i-1]-1;
            change(l,r);
        }
        int ans=1;
        for(int i=0;i<k;i++)
        {
            for(int j=1;j<=num[i];j++)
            {
                a[i][j]+=a[i][j-1];
                int x=a[i][j]+1;
                ans=(ans*x)%mod;
            }
        }
        cout<<ans<<endl;
    }   
    return 0;
}

1003 - Backpack

题意:

        有n个物品和一个容量为m的背包,第i个物品体积为v_i,价值为w_i ,问在恰好装满背包情况下物品价值的最大异或和,若背包不能恰好装满,输出-1;

做法:

        考虑使用动态规划解决,dp[i][j][k] 表示在装入前i个物品后,异或和为j,容量为k的情况是否存在,则dp方程为dp[i][j][k]=dp[i-1][j][k]|dp[i-1][j\oplus v_i][k-w_i] 我们知道这样的复杂度是O(n^3)的,因此我们可以考虑使用bitset将复杂度优化到O(\frac{n^3}{32} ).

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        vector<bitset<1025>> dp(1025),g(1025);
        dp[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            int x,y;
            cin>>x>>y;
            for(int j=0;j<1024;j++)
            {
                g[j]=dp[j];
                g[j]<<=x;
                for(k=0;k<1024;k++)
                    if(g[j][k])
                        cout<<k<<endl;
            }
            for(int j=0;j<1024;j++)
                dp[j]|=g[j^y];
        }
        int ans=-1;
        for(int i=1023;i>=0;i--)
        {
            if(dp[i][m])
            {
                ans=i;
                break;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

1009 - Laser

题意:

        在一个二维坐标系中,有n个敌人,你可以在坐标系中的任何一个坐标中放置一门激光炮,激光炮的射程为任意值,方向可以射向米子型的八个方向,问是否存在一个放置激光炮的位置,使所有的敌人都能被消灭。

做法:我们先假设第一个敌人所在的点为激光炮放置点,遍历剩余n-1个敌人寻找这个点攻击不到的一个点作为第二个点,如果找不到第二个点,说明激光炮放在第一个点可行,输出YES,否则,以第一个点和第二个点为基础,画出第一个点和第二个点的米字型八个方向上的直线所产生的12个交点,这12个交点为如果激光炮能够消灭所有敌人,那么激光炮只能在这十二个点中的其中一个,然后再遍历n个敌人,对每个敌人,判断该敌人是否能够被这12个点中的一个或多个点攻击到,若能的将这个点的消灭敌人数量+1,最后再判断这十二个点是否存在一个点能消灭n个敌人,即消灭敌人数为n,若存在,则输出YES,反之则输出NO。

十二红色点的坐标可一一标出,注意某些点可能为分数,若点为分数则不记录

假设第一次遍历找到的两个为(a,b)(x,y),则这十二个点分别为:

(a-y+b,y),(x+y-b,b),(a,y+x-a),(x,a+b-x),(x,x-a+b),(a,a-x+y)

(a,y),(x,b),(a+y-b,y),(x-y+b,b)((x-y+b+a)/2,(a-x+y+b)/2),((x+a+y-b)/2,(x-a+b+y)/2)

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        vector<P> a(n);
        for(int i=0;i<n;i++)
            cin>>a[i].f>>a[i].s;
        bool flag=true;
        int aa=a[0].f,bb=a[0].s,x,y;
        for(int i=1;i<n;i++)
        {
            if(a[i].s==a[0].s||a[i].f==a[0].f||abs(a[i].s-a[0].s)==abs(a[i].f-a[0].f))
                continue;
            else
            {
                flag=false;
                x=a[i].f;
                y=a[i].s;
            }
        }
        if(flag)
        {
            cout<<"YES"<<endl;
            continue;
        }
        else
        {
            vector<P> point;
            point.emplace_back(aa-y+bb,y);
            point.emplace_back(x+y-bb,bb);
            point.emplace_back(aa,y+x-aa);
            point.emplace_back(x,bb-x+aa);
            point.emplace_back(x,x-aa+bb);
            point.emplace_back(aa,aa-x+y);
            point.emplace_back(aa,y);
            point.emplace_back(x,bb);
            point.emplace_back(aa+y-bb,y);
            point.emplace_back(x-y+bb,bb);
            if((x-y+bb+aa)%2==0&&(aa-x+y+bb)%2==0)
                point.emplace_back((x-y+bb+aa)/2,(aa-x+y+bb)/2);
            if((x+aa+y-bb)%2==0&&(x-aa+bb+y)%2==0)
                point.emplace_back((x+aa+y-bb)/2,(x-aa+bb+y)/2);
            vector<int> vis(point.size(),0);
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<point.size();j++)
                {
                    int xx=point[j].f,yy=point[j].s;
                    if(a[i].s==yy||a[i].f==xx||abs(a[i].s-yy)==abs(a[i].f-xx))
                    {
                        vis[j]++;
                        continue;
                    }
                }
            }
            bool ans=false;
            for(int x1:vis)
            {
                if(x1==n)
                    ans=true;
            }
            if(ans)
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
        }
    }   
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值