2021.09.14 膜你赛
T1
Description
给定一个 \(1 \sim n\) 的排列 \(a_i\) 给出 \(q\) 个询问 \((x,m)\) 他想知道:
\[\underbrace{a[a[a[…a[x]…]]]}_{m 个 a} \]的值是多少
Solution
考试的时候写的 \(n^2\) 的做法。
对于每一个点能跳到的下一个点,我们连一条边,最后序列上一定是一个个环的形式,
暴力找到循环节,输出答案,就是输入有些毒瘤。
Code
/*
* @Author: smyslenny
* @Date: 2021.09.14
* @Title:
* @Main idea:
*复杂度:O(n^2) 能过50
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
const int M=1e+5;
int n,Q,x,m;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<‘0‘ || c>‘9‘) {if(c==‘-‘) y=0;c=getchar();}
while(c>=‘0‘ && c<=‘9‘) { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
int mp[M],ord[M];
struct que{ int x,m;}q[M];
struct ed{int u,v,net;} edge[M];
int head[M],num,vis[M],pa[M];
void addedge(int u,int v) { edge[++num].u=u;edge[num].v=v;edge[num].net=head[u];head[u]=num;}
int solve(int x,int m)
{
memset(pa,0,sizeof(pa));
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
{
if(vis[x]==0)
{
vis[x]=1;
pa[i]=x;
x=head[x];
x=edge[x].v;
}
else
{
m%=(i);
return pa[m];
break;
}
}
return x;
}
int main()
{
// freopen("kengdie.in","r",stdin);
// freopen("kengdie.out","w",stdout);
while(1)
{
x=read();
if(mp[x]==0)
{
ord[++n]=x;
mp[x]=1;
}
else
{
m=read();
// if(m>n) m%=n;
break;
}
}
num=0;
for(int i=1;i<=n;i++)
addedge(i,ord[i]);
printf("%d\n",solve(x,m));
while(scanf("%d%d",&x,&m)!=EOF)
{
// if(m>n) m%=n;
printf("%d\n",solve(x,m));
}
return 0;
}
/*
3
2
1
1
1
2
2
*/
T2
Descrition
给定 \(n\) 个数 \(a_i\) 找到一个子集 \(b_i\) 最大化
\[\sum_{i = 1}^{|b|}b_i \bmod M \]
Solution
看到数据范围果断折半搜索。
上次考试碰到折半搜索写的 dp,这次直接搜索,再合并左右区间就行了。
Code
/*
* @Author: smyslenny
* @Date: 2021.09.
* @Title:
* @Main idea:
* 复杂度 O(2^n) 50分
* O(1e6)
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int M=(1<<21);
int n,mod,mp[M];
int read()
{
int x=0,y=1;
char c=getchar();
while(c<‘0‘ || c>‘9‘) {if(c==‘-‘) y=0;c=getchar();}
while(c>=‘0‘ && c<=‘9‘) { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
namespace substack1{
int las=0,js=0,Ans=-INF,sz[M];
void dfs(int x)
{
if(x>n) return;
sz[x]=js;
js+=mp[x];
js%=mod;
Ans=max(Ans,js);
dfs(x+1);
js=sz[x];
dfs(x+1);
return;
}
void main()
{
dfs(1);
if(Ans==-INF) printf("0\n");
else printf("%d\n",Ans);
}
}
namespace substack2{
int num=0,Ans=0;
void main()
{
for(int i=1;i<=n;i++)
num+=mp[i],num%=mod,Ans=max(Ans,num);
printf("%d\n",Ans);
}
}
namespace substack3{
int sum_1[M],sum_2[M],js_1,js_2,mid,Ans=-INF;
void dfs_1(int x,int num)
{
sum_1[js_1++]=num%mod;
for(int i=x;i>mid;i--)
dfs_1(i-1,(num+mp[i])%mod);
}
void dfs_2(int x,int num)
{
sum_2[js_2++]=num;
for(int i=x;i>=1;i--)
dfs_2(i-1,(num+mp[i])%mod);
}
void main()
{
mid=n>>1;
sort(mp+1,mp+1+n);
dfs_1(n,0);
dfs_2(mid,0);
sort(sum_1,sum_1+js_1);
for(int i=0;i<js_2;i++)
{
int l=0,r=js_1;
while(l<=r)
{
int mid=(l+r)>>1;
if(sum_1[mid]>mod-sum_2[i])
r=mid-1;
else
l=mid+1,Ans=max(Ans,(sum_2[i]+sum_1[mid])%mod);
}
}
printf("%lld\n",Ans);
}
}
signed main()
{
freopen("gift.in","r",stdin);
freopen("gift.out","w",stdout);
n=read(),mod=read();
for(int i=1;i<=n;i++)
mp[i]=read(),mp[i]%=mod;
// if(n<=16) substack1::main();
// substack2::main();//以为理解错题意了,结果发现没理解错题意
// else
substack3::main();
return 0;
}
T3
Description
给定 \(n\) 个数 \(a_i\) 和另外的两个数 \(A, B\) 有两种操作:
执行 \(A = A - 1\)
选择一个 \(1\) 到 \(n\) 之间的数 \(x\) 执行: \(A = A - (A \bmod a_x)\)
求最少的操作次数使 \(A = B\)
Solution
不大会,但是直接输出 \(A-B\) 有十分。