NOIP模拟64
T1 三元组
解题思路
一看题面,好像是一道数学题,但不完全是,或者说根本不是。。。
比较好想到的是 \(\mathcal{O}(n^2)\) 和 \(\mathcal{O}(nk)\) 的做法,然后就有了 60pts。。。
观察范围对于每一个 \(b\) 而言 \(a+b^2\) 的范围就是 \([1+b^2,b+b^2]\) 。
充分利用 \(a\le b\le c\) 这个条件,枚举 \(c\) 然后就对于每一个新加入的 \(c\) , \(a,b\) 的范围也会相应的扩大。
因此可以维护树状数组,对于取模之后的区间进行区间修改,然后单点查询每一个 \(c^3\) 的贡献就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10;
int Test,ans,n,p;
struct BIT
{
int tre[N];
void clear(){memset(tre,0,sizeof(tre));}
int lowbit(int x){return x&(-x);}
void insert(int x,int val){for(int i=x+1;i<=p;i+=lowbit(i))tre[i]+=val;}
int query(int x){int sum=0;for(int i=x+1;i;i-=lowbit(i))sum+=tre[i];return sum;}
void insert(int l,int r,int val){insert(l,val);insert(r+1,-val);}
}T;
void solve()
{
n=read(); p=read(); ans=0; T.clear();
for(int i=1;i<=n;i++)
{
int l=(1+i*i)%p,r=(i+i*i)%p,temp=i/p;
T.insert(0,p-1,temp);
if(i%p&&l<=r) T.insert(l,r,1);
else if(i%p&&l>r) T.insert(l,p-1,1),T.insert(0,r,1);
ans+=T.query(i*i*i%p);
}
printf("%lld\n",ans);
}
signed main()
{
freopen("exclaim.in","r",stdin); freopen("exclaim.out","w",stdout);
Test=read(); for(int i=1;i<=Test;i++) printf("Case %lld: ",i),solve();
return 0;
}
T2 简单的字符串
解题思路
打了一种假做法,但是好像不是特别可以被卡掉(吸氧的前提下)
\(n^3\) 的做法加上字符集优化(也就是无序 Hash 即可)。
当然为了防止被 Hack ,于是我加了一个 map
记忆化一下
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e3+10;
const ull base=133331ull;
int n,ans,s[N];
ull has,has2,hs[N],p[N],pre[N];
map< pair<ull,ull> , bool > mp;
ull power(ull x,int y)
{ull temp=1;while(y){if(y&1) temp=temp*x;x=x*x; y>>=1;}return temp;}
ull get(int l,int r){return pre[r]-pre[l-1]*p[r-l+1];}
signed main()
{
freopen("s.in","r",stdin); freopen("s.out","w",stdout);
n=read(); p[0]=1;
for(int i=1;i<=n;i++)
s[i]=read(),pre[i]=pre[i-1]*base+s[i],
p[i]=p[i-1]*base,hs[i]=hs[i-1]+power(s[i],base);
for(int len=2;len<=n;len+=2)
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1,mid=(l+r)>>1; has=pre[r]-pre[mid]*p[len/2]; has2=pre[mid]-pre[l-1]*p[len/2];
if(hs[r]-hs[mid]!=hs[mid]-hs[l-1]) continue;
if(has==has2||mp.find(make_pair(has,has2))!=mp.end()){ans++;continue;}
for(int i=l;i<=mid;i++)
{
ull ha1=get(l,i),ha2=get(i+1,mid),ha=ha1+ha2*p[i-l+1];
if(has==ha)
{
if(has>has2) swap(has,has2);
mp.insert(make_pair(make_pair(has,has2),true));
ans++;break;
}
}
}
printf("%lld",ans);
return 0;
}
T3 环路
解题思路
矩阵快速幂
矩阵 \(A_{i,j}\) 表示 i 到 j 是否有连边,也就是初始读入的矩阵, B 为单位矩阵。
于是我们就可以快速利用矩阵快速幂算出每个点在 k 步可以到达的点,但是这样显然需要记录到每一个状态的答案。
考虑建假点,对于 \(i\) 点建一个 \(i+n\) 作为假点,每个点向相对应的假点连一条边,同样的假点也要向自己连边。
最后答案就是 从 \(i\) 到 \(i+n\) 的方案数减去 \(n\) 也就是没有移动的方案数
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=210;
int n,m,mod,ans;
char ch[N];
struct Square
{
int a[N][N];
void clear(){memset(a,0,sizeof(a));}
Square friend operator * (Square x,Square y)
{
Square z; z.clear();
for(int i=1;i<=2*n;i++)
for(int j=1;j<=2*n;j++)
for(int k=1;k<=2*n;k++)
z.a[i][j]+=x.a[i][k]*y.a[k][j]%mod;
for(int i=1;i<=2*n;i++)
for(int j=1;j<=2*n;j++)
z.a[i][j]%=mod;
return z;
}
}A,B;
void power(Square &x,Square e,int y){while(y){if(y&1)x=x*e;e=e*e;y>>=1;}}
signed main()
{
freopen("tour.in","r",stdin); freopen("tour.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1); B.a[i][i]=B.a[i+n][i+n]=A.a[i][i+n]=A.a[i+n][i+n]=1;
for(int j=1;j<=n;j++) A.a[i][j]=(ch[j]=='Y');
}
m=read(); mod=read(); power(B,A,m);
for(int i=1;i<=n;i++) ans+=B.a[i][i+n];
printf("%lld",(ans-n+mod)%mod);
return 0;
}
T4 过河
解题思路
很玄学的一道题,从考试开始到结束看这个题的思路只有 模拟,模拟,再模拟,然后喜提 0pts
对于 60pts 的做法直接暴力枚举在运送神猪(就是所有三元组都有的猪)前后所运送的两只猪判断剩下的关系是否是二分图就好了。
进行一些优化只枚举一只,查看剩下的图是否可以通过删掉一个点成为二分图。
因此我们可以高出一个 DFS 树,一个合法的点的充要条件就是被所有奇数长度的环经过,并且不可以存在一个奇数环或者偶数环一段在子树中,另一端在当前点的上方。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e3+10,INF=1e18;
int T,n,m,jud,flag,pos,all,dep[N],fa[N],odd[N],even[N],minn[N];
int tot=1,head[N],nxt[N<<1],ver[N<<1];
bool vis[N];
bitset<N> bit;
struct Node{int a,b,c;}s[N];
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x)
{
dep[x]=dep[fa[x]]+1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
if(!dep[to])
{
fa[to]=x,dfs(to),odd[x]+=odd[to];
even[x]=min(even[x],even[to]),minn[x]=min(minn[x],minn[to]);
}
else if((dep[x]-dep[to])&1)
{
if(dep[x]>dep[to])
even[x]=min(even[x],dep[to]);
}
else if(dep[to]<dep[x])
odd[x]++,odd[fa[to]]--,all++,minn[x]=min(minn[x],dep[to]);
}
}
void solve()
{
n=read(); m=read(); bit.reset(); flag=false;
for(int i=1;i<=m;i++) s[i].a=read(),s[i].b=read(),s[i].c=read();
bit[s[1].a]=bit[s[1].b]=bit[s[1].c]=true;
for(int i=2;i<=m;i++)
{
bitset<N> b; b.reset(); b[s[i].a]=b[s[i].b]=b[s[i].c]=true;
if(!(bit&b).count()) return printf("no\n"),void(); bit&=b;
}
for(int i=1;i<=n;i++) if(bit[i]){pos=i;break;}
for(int i=1;i<=n&&!flag;i++)
{
tot=1; all=0;
for(int j=1;j<=n;j++) head[j]=odd[j]=dep[j]=fa[j]=0,even[j]=minn[j]=INF;
for(int j=1;j<=m;j++)
{
int a=s[j].a,b=s[j].b,c=s[j].c;
if(a==pos&&b!=i&&c!=i) add_edge(b,c),add_edge(c,b);
else if(b==pos&&a!=i&&c!=i) add_edge(a,c),add_edge(c,a);
else if(c==pos&&a!=i&&b!=i) add_edge(a,b),add_edge(b,a);
}
for(int j=1;j<=n;j++) if(j!=i&&j!=pos&&!dep[j]) dfs(j);
for(int j=1;j<=n&&!flag;j++)
if(odd[j]==all)
{
jud=true;
for(int k=head[j];k&&jud;k=nxt[k])
{
int to=ver[k];
if(dep[to]>dep[j]&&even[to]<dep[j]&&minn[to]<dep[j]) jud=false;
}
flag|=jud;
}
}
if(flag) printf("yes\n"); else printf("no\n");
}
signed main()
{
freopen("river.in","r",stdin); freopen("river.out","w",stdout);
T=read(); while(T--) solve(); return 0;
}