GDCPC广东省大学生程序设计竞赛

比赛链接

GDCPC广东省大学生程序设计竞赛

A.An Easy Problem

给你三个数n,m,k
然后会根据n,m生成数列
1×1,1×2,······,1×m
2×1,2×2,······,2×m
···
n×1,n×2,······,n×m
问你将这些数字中第k大的数字是多少

解题思路

二分

AcWing 4080. 第k个数

  • 时间复杂度:O(n+m)log(nm)

大根堆

(1×m,1),(2×m,2),(3×m,3),,(n×m,n) 放入大根堆中,每个二元组负责 m 个数,这样所有的数都能兼顾到,且初始时数最大,如 (n×m,n) 负责 n×1,n×2,,n×m 这些数,当前最大数要求在堆中,将最大数 (i×j) 弹出,同时将 (i×ji,i) 压入堆中,此时最大值肯定仍在堆中,弹出 k1 次即得 k 大数
注意 k 不会很大,数列中实际上会有很多重复数

  • 时间复杂度:O(nlogn+klogn)

代码

// Problem: An Easy Problem // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/A // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } int n,m,k; priority_queue<pair<LL,int>> q; int main() { cin>>n>>m>>k; for(int i=1;i<=n;i++)q.push({1ll*i*m,i}); while(--k) { auto t=q.top(); q.pop(); q.push({t.fi-t.se,t.se}); } cout<<q.top().fi; return 0; }

B.Byfibonacci

T 组询问,求出 w(F(x)),用不同斐波那契数构成的 x 的方案的价值的和,方案的价值为每个斐波那契数的乘积

解题思路

dp

首先有斐波那契数的一个前缀和性质:s[x]=f[x+2]+1,故大概对于任何一个数 x,其由不同斐波那契数组成的数的最大数要么是小于等于该数的最大值,要么是小于等于该数的次大值, 设小于等于 x 的斐波那契数的最大值为 f[i],组成 x 的斐波那契数的最大值为小于等于 x 的次次大值 f[i2],则有 s[i2]=f[i]+1f[i]xs[i2]1x,即 s[i2]<x,而由于小于等于 x 的最大斐波那契数为 f[i2],则 xs[i2],与 s[i2]<x 矛盾,故假设不成立,所以最大数是最大值与次大值两者取一,注意不能同时取,如果同时取的话,由斐波那契数列,f[i+1]=f[i]+f[i1],即同时取的时候,最大的那个斐波那契数应该是 f[i+1],与定义的 f[i] 矛盾,故只能两者取一,利用这一点进行 dp 转移即可,另外注意在取较大值 f[i] 时,可能选取的 xf[i] 里面会有等于 f[i] 的元素,而要求的是不同元素,所以在转移时当出现相同元素时这部分不应该统计进答案,即判断小于等于 xf[i] 的最大和次大的斐波那契数是否等于 f[i]。另外简单证明一下为什么选取最大数 f[i] 时,xf[i] 里面不会出现 f[i],设 xf[i] 里面出现了 f[i],则 f[i]xf[i]2f[i]xf[i]+f[i1]=f[i+1]2f[i]x,则小于等于 x 的最大斐波那契数应该是 f[i+1] 才对,与 f[i] 的定义矛盾,故假设不成立,证毕

p 为小于等于 x 的最大的斐波那契数的下标,则

  • 状态表示:

    • f[x][0] 表示 x 选取最大的那个斐波那契数的乘积和
    • f[x][1] 表示 x 选取次大的那个斐波那契数的乘积和

满足要求的斐波那契数才 3 个,所以二分寻找小于等于 x 的最大的斐波那契数的复杂度可以忽略不计,则:

  • 时间复杂度:O(n)

代码

// Problem: Byfibonacci // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/B // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e7+5,mod=998244353; int t,n,a[50],cnt=2,f[N][2]; int main() { f[0][0]=f[1][0]=f[1][1]=a[0]=a[1]=1; while(a[cnt-1]+a[cnt-2]<=1e7)a[cnt]=a[cnt-1]+a[cnt-2],cnt++; cnt--; for(int i=2;i<=1e7;i++) { int p=lower_bound(a,a+cnt+1,i)-a; if(a[p]!=i)p--; f[i][0]=1ll*(f[i-a[p]][0]+f[i-a[p]][1])*a[p]%mod; int pp=lower_bound(a,a+cnt+1,i-a[p-1])-a; if(a[pp]!=i-a[p-1])pp--; if(a[pp-1]!=a[p-1])f[i][1]=1ll*f[i-a[p-1]][1]*a[p-1]%mod; if(a[pp]!=a[p-1])f[i][1]=(f[i][1]+1ll*f[i-a[p-1]][0]*a[p-1]%mod)%mod; } for(read(t);t;t--) { read(n); printf("%d\n",(f[n][0]+f[n][1])%mod); } return 0; }

C.Conspicuousness

给定一个由 19 组成的长度范围为(3×105)的字符串,并给出 qq<=3×105 次询问,每次给出个长度 k, 求出该字符串中长度至少为 k 的子串的最多出现次数

解题思路

后缀自动机

后缀自动机裸题,本题拿后缀自动机中的树求解即可,即通过树求得 enspos[i],其中 i 表示状态节点的大小,用其更新长度 node[i].len 的贡献,虽然 node[i].len 表示的是状态节点 i 里面表示的子串的最长长度,但由于最后求解的是至少长度为 k 的子串的最多出现次数,且长度越小的子串的出现次数越多,所以需逆序更新最大值,故可以直接拿 node[i].len 更新答案

  • 时间复杂度:O(n)

代码

// Problem: Conspicuousness // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/C // Memory Limit: 524288 MB // Time Limit: 4000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=3e5+5; int n,q,endpos[N<<1],f[N<<1],cnt=1,lst=1; vector<int> adj[N<<1]; char s[N]; struct Node { int len,fa; int ch[10]; }node[N<<1]; void extend(int c) { int p=lst,np=lst=++cnt; node[np].len=node[p].len+1; endpos[np]=1; for(;p&&!node[p].ch[c];p=node[p].fa)node[p].ch[c]=np; if(!p)node[np].fa=1; else { int q=node[p].ch[c]; if(node[q].len==node[p].len+1)node[np].fa=q; else { int nq=++cnt; node[nq]=node[q]; node[nq].len=node[p].len+1; node[np].fa=node[q].fa=nq; for(;p&&node[p].ch[c]==q;p=node[p].fa)node[p].ch[c]=nq; } } } void dfs(int x) { for(int y:adj[x]) { dfs(y); endpos[x]+=endpos[y]; } f[node[x].len]=max(f[node[x].len],endpos[x]); } int main() { scanf("%d%d",&n,&q); scanf("%s",s); for(int i=0;s[i];i++)extend(s[i]-'0'); for(int i=1;i<=cnt;i++)adj[node[i].fa].pb(i); dfs(1); for(int i=n;i>=1;i--)f[i]=max(f[i],f[i+1]); while(q--) { int x; scanf("%d",&x); printf("%d\n",f[x]); } return 0; }

D.Double

打架游戏,给出 n 个战斗力。战斗力大的能战胜小的,相等的胜率一半,并且只能和相邻的战斗,每次胜利之后战斗力会翻倍,要求输出所有可能胜利到最后的人的序号

解题思路

思维

由于每次翻倍且范围为 1,109,所以对于一个数来说,操作能大于等于 30 次就胜利了,故对于一个数可以左右操作直到左右都不能操作或者操作数大于等于 30 时停止对该数的操作

  • 时间复杂度:O(30n)

代码

// Problem: Double // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/D // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=5e5+5; int n,a[N]; int main() { cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; vector<int> res; a[0]=a[n+1]=0x3f3f3f3f; for(int i=1;i<=n;i++) { LL s=a[i]; int l=i,r=i; bool f=false; while(true) { while(l-1>=1&&a[l-1]<=s)s<<=1,l--; while(r+1<=n&&a[r+1]<=s)s<<=1,r++; if(l==1&&r==n||i-l+r-i>=30) { f=true; break; } if(a[l-1]>s&&a[r+1]>s)break; } if(f)res.pb(i); } cout<<res.size()<<'\n'; for(int i:res)cout<<i<<' '; return 0; }

F.Fake Math Problem

给出一个序列 a0,a1,an,求 i=0nj=0aiP(i,j)mod998241383,其中 P(i,j)=i(i1)(i2)(ij+1)j factors 

n(1n105)a0,a1,,an(0aii)

解题思路

暴力

关键点在于 998241383=6739371583,乘法计算最多乘 1583 次后面模 998241383 就为 0 了,然后暴力即可

  • 时间复杂度:O(1583n)

代码

// Problem: Fake Math Problem // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/F // Memory Limit: 524288 MB // Time Limit: 6000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int mod=673*937*1583; int t,n,x,res; int main() { for(cin>>t;t;t--) { res=0; cin>>n; for(int i=0;i<=n;i++) { cin>>x; int t=1; for(int j=0;j<=x&&t;j++) { res=(res+t)%mod; t=1ll*t*(i-j)%mod; } } cout<<res<<'\n'; } return 0; }

G.Good Game, GG

AliceBob 玩一场游戏,给定 n 个整数。
Alice 有两种操作:
① 选一个奇数,将其分成两个整数
② 拿掉一个 1
Bob 只有 1种操作:选择一个偶数并且将这个偶数拆分成两个数

Alice 为先手,判断谁是winner。

解题思路

博弈论

首先对于 1,只能 Alice 操作而 Bob 不能操作,对 2Bob 只能将其拆成 1,1,这样反而 Alice 还比 Bob 多操作一次,Bob 肯定不会操作 2,故相当于 2 没用,而 =+,故对于 Alice 来说,如果 x1 的话,其必定会构造出一个偶数,这样最好是给 Bob 构造无用的 2,其操作数为 x+12;而对于偶数 xBob 一定会划分为两个偶数,以划分为 x2,2 为例,其操作数为 x21,比较两操作数即可

  • 时间复杂度:O(n)

代码

// Problem: Good Game, GG // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/G // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } int t,n,x; int main() { for(cin>>t;t;t--) { cin>>n; LL cnt1=0,cnt2=0; while(n--) { cin>>x; if(x&1)cnt1+=x+1>>1; else cnt2+=x/2-1; } puts(cnt1>cnt2?"Alice":"Bob"); } return 0; }

H.History

给定一张 n 个点,m 条带权边的图,每到一个点所有边权 x 都根据 next(x)=1+x1x%p(其中 p 为质数)变化,求最短路

1n,m2×105,5p109

解题思路

分层图

考虑 next(x)=1+x1x 式子的变化,设 x=tan(t),则 next(tan(t))=1+tan(t)1tan(t)=tan(t+π4),而 tan(t)=tan(t+π),故权值为周期为 4 的一个循环,这样可利用分层图求解。这与一般的分层图不同,一般的分层图从建图入手,而这里从设置状态入手,即用 d[i][j] 表示到节点 i 且在 j 层的最短路,当然层数不好设置,但由于只有 4 个权值,所以需将 j4 取模

  • 时间复杂度:O(4×(mlog(n)+log(p))

代码

// Problem: History // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/36906/H // Memory Limit: 524288 MB // Time Limit: 4000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> // #define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2e5+5; int n,m,p; LL d[N][4]; vector<PII> adj[N]; struct Node { LL dis; int x,d; }; bool operator<(Node a,Node b) { return a.dis>b.dis; } int ksm(int a,int b,int p) { int res=1%p; while(b) { if(b&1)res=1ll*res*a%p; a=1ll*a*a%p; b>>=1; } return res; } int cal(int x,int t) { int res=x; for(int i=0;i<t;i++)res=1ll*(1+res)*ksm(((1-res)%p+p)%p,p-2,p)%p; return res; } void dijkstra() { priority_queue<Node> q; memset(d,0x3f,sizeof d); q.push({0,1,0}); d[1][0]=0; while(q.size()) { auto t=q.top(); q.pop(); int x=t.x,pos=t.d; if(d[x][pos]!=t.dis)continue; for(auto t:adj[x]) { int y=t.fi,w=t.se; int npos=(pos+1)%4; int nw=cal(w,pos); if(d[y][npos]>d[x][pos]+nw) { d[y][npos]=d[x][pos]+nw; q.push({d[y][npos],y,npos}); } } } } int main() { read(n),read(m),read(p); for(int i=1;i<=m;i++) { int x,y,z; read(x),read(y),read(z); adj[x].pb({y,z}); } dijkstra(); LL res=1e18; for(int i=0;i<4;i++)res=min(res,d[n][i]); cout<<res; return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16439309.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(217)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示