杭电多校 2023“钉耙编程”中国大学生算法设计超级联赛(2)

难度不分先后

1007 foreverlasting and fried-chicken

我发图吧这个题赛中告诉队友做法了这里就不重复了

杭电多校 2023“钉耙编程”中国大学生算法设计超级联赛(2)_第1张图片

然后你就会发现上面直接搜点就会tle

想优化吧,用bitset

如果 x点能到达得点和y点相同,那么就可以做为中间的点,

然后贡献就是代码里面了 如果a能到达b,就减去这条边,我画个图吧

杭电多校 2023“钉耙编程”中国大学生算法设计超级联赛(2)_第2张图片

#include
using namespace std;
const int N = 1010,mod=1e9+7;
typedef pair PII;
#define int long long
typedef long long LL;

int n,m,k;
int fact[N],infact[N];
bitset g[N];
int qmi(int a, int k, int p)  // 求a^k mod p
{
    int res = 1 % p;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
void init(){
    fact[0]=infact[0]=1;
    for(int i=1;ia) return 0;
    return (LL)fact[a] * infact[b] % mod * infact[a - b] % mod;
}
void solve()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) g[i].reset();
    for(int i=1;i<=m;i++){
        int a,b;cin>>a>>b;
        g[a].set(b),g[b].set(a);
    }
    int res=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            auto tmp=g[i]&g[j];
            int cnt=tmp.count();
            if(tmp.count()>=4)
            {
                int a=g[i].count(),b=g[j].count();
                if(g[i][j]) a--,b--;
                if(a>=6){
                    res+=C(cnt,4)*C(a-4,2);
                    res%=mod;
                }
                if(b>=6){
                    res+=C(cnt,4)*C(b-4,2);
                    res%=mod;
                }
            }
        }
    }
    cout<>t;
    while(t--) solve();
}
1002 Binary Number

首先手玩题

画画图找一下规律?

比如 1001001 让他变成全1

翻转区间[2,6] 再翻转[4,4]可以变成1

但是可以发现直接翻转 [2,3] [5.6]等价且实现简单

然后特判 k=1和n=1得情况这种情况很坑

#include
using namespace std;
const int N = 3e6+10;
typedef pair PII;
#define int long long
long long n,m,k;
int a[N];
void solve()
{
    cin>>n>>k;
    int r=0,l=0x3f3f3f3f;
    string s;cin>>s;
    if(n==1)
    {
        int x=k%2;
        int res=s[0]-'0';
        if(x&1) res^=1;
        cout<>t;
    while(t--) solve();
}

1009 String Problem

因为选的每个字符串只有一个字母,所以直接全选,并且选的连续长度越长越好

我直接双指针了

#include
using namespace std;
const int N = 3e6+10;
typedef pair PII;
#define int long long
int n,m,k;
int a[N];
void solve()
{
    string s;
    cin>>s;n=s.size();
    vector cnt(30,0);
    int res=0;
    for(int i=0;i1)
        {
            i=j-1;    
        }
        
    }

    cout<>t;
    while(t--) solve();
}
1010 Klee likes making friends

额应该很明显的dp?

然后怎么想呢我这边建议是画图想

首先看范围n=2e4 ,m=20很明显叫我循环n*m,

我直接定义了状态前i个人满足连续m个至少有两个好朋友,且必选第i个人,和前面第二个人的距离是j(因为你要至少两个人,所以你状态要可以表达出两个人的位置)

考虑初始化,直接初始化第一个m的连续区间的数,就是两个人的值了

状态怎么转移呢,

f[i][j]=a[i]+sum[i-j][m-j]; (j是枚举和当前i的距离,所以他的位置就是i-j了)

i-j到i中间是没数的因为他们表示最后一个人位置是i,倒数第二个人位置是i-j

所以第三个人位置是i-m到i-j-1,下面的图你可以看出来,不算7的话你要保证6结尾的连续m合法

所以第三个人一定在倒数第二个人人中我画的那里,他们的距离可以是1,2,3...,m-j

所以方程就可以列出来了,那么考虑优化

我们只要求第三个人的距离是1,2,3...,m-j,所以求他们的最小值就行,我们直接求前缀最小值即可,然后交了,可对但是会mle,因为第一维是n=2e4

下面的第一个代码就是我说的mle的代码

第二个代码是优化后的代码

考虑优化空间,我们只需要知道前面m个人的信息就好了,我们直接用取模方式优化空间即可,其他没啥变化

杭电多校 2023“钉耙编程”中国大学生算法设计超级联赛(2)_第3张图片

#include
using namespace std;
const int N = 2e4+10;
typedef pair PII;
int n,m,k;
int a[N];
int f[N][2010];
int sum[N][2010];

void solve()
{
    cin>>n>>m;
    int res=2e9;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
        sum[i][0]=2e9;
        for(int j=1;j<=m;j++){
            if(i-j<=0) f[i][j]=2e9;
            else f[i][j]=a[i]+a[i-j];
            sum[i][j]=min(sum[i][j-1],f[i][j]);
        }
    }
    for(int i=m+1;i<=n;i++)
    {
        sum[i][0]=2e9;
        for(int j=1;j<=m;j++){
            f[i][j]=a[i]+sum[i-j][m-j];
            sum[i][j]=min(sum[i][j-1],f[i][j]);
        }
    }
    for(int i=1;i<=m;++i)    res=min(res,sum[n+1-i][m-i]);
    cout<>t;
    while(t--) solve();
}
#include
using namespace std;
const int N = 2e4+10;
typedef pair PII;
int n,m,k;
int a[N];
int f[2010][2010];
int sum[2010][2010];
int tdp[2010],tsum[2010];
void solve()
{
    cin>>n>>m;
    int res=2e18;
    for(int i=0;i>a[i];
    for(int i=0;i>t;
    while(t--) solve();
}
1004 Card Game

这里写的歪解,看范围n=1e9,基本是个结论题,直接看答案...

然后就可以直接得出结论等于2^(n-1)-1

问就是猜的,补题也不准备想....

#include
using namespace std;
const int N = 2e4+10,mod=998244353;
#define int long long
typedef pair PII;
typedef long long LL;

int n,m,k;
int a[N];
int qmi(int a, int k, int p)  // 求a^k mod p
{
    int res = 1 % p;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

void solve()
{
    cin>>n;
    cout<<(((qmi(2,n-1,mod)-1)%mod)+mod)%mod<<"\n";
}
// 2 3  1 3  3 
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
}
1011 SPY finding NPY

首先k=0的时候,概率是1/n,每个人是最大值的概率是1/n

把每个不同k分开独立思考

对于当前k来说,能选到最大值的概率是 1/n*(k+1)*P+1/n*(k+2)*P+...*1/n*(n)*P的和

期望定义嘛0.0 E(ax+by)=aE(X)+bE(Y)

P是不同的,为了保证在当前i位一定能被选到,所以1到k里面的最大值要是1到i-1里面的最大值

所以P=k/(i-1)

杭电多校 2023“钉耙编程”中国大学生算法设计超级联赛(2)_第4张图片

然后就把未知数提取出来然后枚举k o(1)计算即可,要预处理1/i的前缀和 

#include
using namespace std;
const int N = 2e5+10,mod=998244353;
#define int long long
typedef pair PII;
typedef long long LL;

int n,m,k;
int a[N];
double sum[N];
void init(){
    for(int i=1;i>n;
    int ans=0;
    double p=1.0/n;
    for(int i=1;i<=n-1;i++){
        double now=calc(n,i);
        if(now>p){
            ans=i,p=now;
        }
    }
    cout<>t;
    while(t--) solve();
}
1012 Coin

额虽然感觉可能看不太出是网络流的题,但他确实是最大流模板题...

首先有个知识就是拆点,一个点拆成两个点

科普小知识

将每个人拆成一个入点和一个出点,原先连向它的边变成连向入点的边,原先从它连出去的边变成从出点连出去的边。然后从入点向出点连一条容量为 自身限制的流量的边,保证再多的流量流向这个人,在经过中间这条容量为都不会超过他的上限

然后可以想怎么建图了,因为一开始每个人只有一块钱,所以起点连向每个点流量的边都是1

因为要按照询问顺序连边,我们需要记录自身点上一次询问操作过的点的编号,进行拆点,再连向A,B,流量都是1,然后最后把所有点的最后顺序点的编号都连向终点即可

然后直接copy dinic模板就能过了

大概就这样图....,如果没学过网络流的话...建议直接跳了把这题

杭电多校 2023“钉耙编程”中国大学生算法设计超级联赛(2)_第5张图片

 

#include
using namespace std;
const int N = 3e4+10,mod=998244353,M=3*N;
typedef pair PII;
typedef long long LL;
#define S (N*2+1)
#define T (N*2+2)
#define INF 0x3f3f3f3f
int n,m,k;
int h[M],e[500010],ne[500010],f[500010],idx,pn;
int q[M],d[M],cur[M];
int p[M],a[N];
void add(int a, int b, int c,int w=0)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = w, ne[idx] = h[b], h[b] = idx ++ ;
}

bool bfs(){
    int hh=0,tt=0;
    memset(d,-1,sizeof(d));
    q[S]=0,d[S]=0,cur[S]=h[S];
    q[0]=S;
    while(hh<=tt){
        int t=q[hh++];
        for(int i=h[t];~i;i=ne[i]){
            int j=e[i];
            if(d[j]==-1&&f[i]){
                d[j]=d[t]+1;
                cur[j]=h[j];
                if(j==T) return true;
                q[++tt]=j;
            }
        }
    }
    return false;
}

int find(int u,int limit){
    if(u==T) return limit;
    int flow=0;
    for(int i=h[u];~i&&flow>n>>m>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    pn=0;
    memset(p,0,sizeof(p));
    memset(h,-1,sizeof(h));idx=0;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        p[x]?add(p[x],++pn,a[x]):add(S,++pn,1);
        p[x]=pn;
        p[y]?add(p[y],++pn,a[y]):add(S,++pn,1);
        p[y]=pn;
        add(p[x],p[y],1,1);
    }
    for(int i=1;i<=k;i++){
        int x;cin>>x;
        if(!p[x]) add(S,T,1);
        else add(p[x],T,a[x]);
    }
    cout<>t;
    while(t--) solve();
}

你可能感兴趣的:(算法)