2022.9.22测试
一测:396pts,不知道为什么哈希假了。
T1:P7800 [COCI2015-2016#6] PAROVI(绿)
T2:P7859 [COCI2015-2016#2] GEPPETTO(绿)
T3:P7861 [COCI2015-2016#2] SAVEZ(蓝)(96pts)
T4:P7862 [COCI2015-2016#2] DRŽAVA(紫)
T1:
读完题后可以首先发现,编号
而对于中间的数如果存在两对数
那么只有
由于求方案数,考虑dp。
预处理出来所有互质的数对,并按左区间为关键字排序。
设
初始化:对于数对
转移:
选择这个区间:
不选这个区间:
答案即为
复杂度约为
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int mod=1e9;
const int N=25;
int n,cnt,f[N*N][N];
int gcd(int x,int y)
{
if(y==0)return x;
return gcd(y,x%y);
}
struct node
{
int l,r;
}a[N*N];
int cmp(node fi,node se)
{
return fi.r>se.r;
}
signed main()
{
//freopen("parovi.in","r",stdin);
//freopen("parovi.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)if(gcd(i,j)==1)a[++cnt]=(node){i,j};
}
for(int i=1;i<=cnt;i++)
{
if(a[i].l==1)f[i][a[i].r]=1;
for(int j=1;j<=n;j++)
{
f[i][j]+=f[i-1][j];
f[i][j]%=mod;
if(a[i].l<=j)f[i][max(j,a[i].r)]+=f[i-1][j],f[i][max(j,a[i].r)]%=mod;
}
}
int ans=f[cnt][n];
printf("%lld",ans);
return 0;
}
T2:
暴搜,完了。
对于每一个材料,只有选或不选,搜索时记录一下选择情况,再进行判断是否有冲突即可。
加个小优化,对于每一对冲突,按材料编号更大的排序,然后每次判断是否合法时,如果冲突的材料还未搜索到,就可以不用继续判断了。
复杂度
为什么习惯签到题放第二题啊。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=405;
const int N=25;
struct node
{
int x,y;
}a[M];
int cmp(node fi,node se)
{
return fi.y<se.y;
}
int n,m,vis[N];
int dfs(int x)
{
for(int i=1;i<=m;i++)
{
if(a[i].y>=x)break;
if(vis[a[i].x]&&vis[a[i].y])return 0;
}
if(x==n+1)return 1;
int sum=0;
vis[x]=0;
sum+=dfs(x+1);
vis[x]=1;
sum+=dfs(x+1);
return sum;
}
int main()
{
//freopen("geppetto.in","r",stdin);
//freopen("geppetto.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
if(a[i].x>a[i].y)swap(a[i].x,a[i].y);
}
sort(a+1,a+1+m,cmp);
int ans=dfs(1);
printf("%d",ans);
return 0;
}
T3:
做这题的心路历程十分神奇。
看到题,想到哈希匹配。
对于每一个
然后本机上跑不过,就进行了分裂循环,优化调用 map 次数等等一系列优化。
结果发现数据比我想象中的水的多。
还是放一下第一次做的代码:代码
设
然后利用 map 存储之前每个字符串的哈希值,最后如果本次前缀和后缀都能匹配上之前的哈希值,那么可以进行转移。
最后求
复杂度
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define int long long
using namespace std;
const int N=2e6+5;
const int mod1=1e15+37;
const int mod2=212370440130137957ll;
map<int,int>vis1,vis2;
int n,dp[N];
char s[N];
inline void read()
{
int cnt=0;
char ch=getchar();
while(ch==' '||ch=='\n')ch=getchar();
while(ch!=' '&&ch!='\n')
{
s[++cnt]=ch;
ch=getchar();
}
s[++cnt]='\0';
}
signed main()
{
//freopen("savez.in","r",stdin);
//freopen("savez.out","w",stdout);
scanf("%lld",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
int len=strlen(s+1);
int num1=0,num2=0,lim1=1,lim2=1;
int num3=0,num4=0;
dp[i]=1;
for(int j=1;j<=len;j++)
{
num1=num1+(s[j]-'A'+2)*lim1;
num1%=mod1;
num2=num2+(s[j]-'A'+2)*lim2;
num2%=mod2;
num3=num3*30+(s[len-j+1]-'A'+2);
num3%=mod1;
num4=num4*30+(s[len-j+1]-'A'+2);
num4%=mod2;
lim1*=30;
lim1%=mod1;
lim2*=30;
lim2%=mod2;
int _1=vis1[num1],_2=vis2[num2],_3=vis1[num3],_4=vis2[num4];
if(_1&&_3&&_1==_2&&_2==_3&&_3==_4)dp[i]=max(dp[i],dp[_1]+1);
}
vis1[num1]=i,vis2[num2]=i;
ans=max(ans,dp[i]);
}
printf("%lld",ans);
return 0;
}
T4:
适当进行骗分是真的有用。
对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。
设
每次将
再从
当合并后
非正解做法(这里注重考场上没想出来如何骗分,看正解可以移步其他题解,注:目前这个解法可以过):
一来想不太出来,但是推出个结论,要推出模数
证明:如果一个新的数加进来,不会产生其他模数,那么原来的模数是个等差数列,并且等差数列会从
那么可以想到其实只需要求一个点离的最近的
如果在考场想不到二分什么的,应该考虑揣摩出题人心理,进行关键字排序。
对于每个点
以
以
以
以
以
……
好吧当我没说。
这里选择第三种排序,排序后取在自己后面的点,这里因为
最后与上面做法就一样了,多加了个启发式合并,对于单独一个点的并查集插入进行特殊处理,不用也可以过。
总结:如果考试想不出来,利用一些非正常办法也可以拿高分的。
复杂度
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
const int N=5e4+5;
int n,k,cnt,f[N],siz[N],s[N][35],q[N];
struct node
{
int x,y,data;
}a[N];
int cmp(node fi,node se)
{
return fi.x*fi.x+fi.y*fi.y<se.x*se.x+se.y*se.y;
}
struct node2
{
int from,to,data;
}t[N*40];
int cmp2(node2 fi,node2 se)
{
return fi.data<se.data;
}
int afind(int x)
{
if(f[x]==x)return x;
return f[x]=afind(f[x]);
}
int krus()
{
for(int i=1;i<=cnt;i++)
{
int from=afind(t[i].from),to=afind(t[i].to);
if(from==to)continue;
if(siz[to]==1)swap(from,to);
for(int j=1;j<k;j++)q[j]=0;
for(int j=1;j<k;j++)
{
if(!s[to][j])continue;
if(siz[from]==1)q[(j+a[from].data)%k]=1,q[a[from].data]=1;
else
{
for(int p=1;p<k;p++)
{
if(!s[from][p])continue;
q[(j+p)%k]=1;
q[p]=1;
}
}
}
for(int j=0;j<k;j++)s[to][j]|=q[j];
siz[to]+=siz[from];
f[from]=to;
if(s[to][0])return t[i].data;
}
return t[cnt].data;
}
signed main()
{
//freopen("drzava.in","r",stdin);
//freopen("drzava.out","w",stdout);
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].data),a[i].data%=k;
sort(a+1,a+1+n,cmp);
int kt=2000000ll/n;
for(int i=1;i<=n;i++)
{
f[i]=i;
siz[i]=1;
s[i][a[i].data]=1;
for(int j=i+1;j<=min(n,i+kt);j++)t[++cnt]=(node2){i,j,(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)};
}
sort(t+1,t+1+cnt,cmp2);
double ans=sqrt(1.0*krus());
printf("%.3lf",ans);
return 0;
}
/*
6 11
0 0 1
0 1 2
1 0 3
1 1 4
5 5 1
20 20 10
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】