noip模拟64
A. 三元组
签到题,树状数组乱写就行了.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll long long int
#define ull unsigned ll
#define lf double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll res=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return cit?res:-res;
}
} using namespace BSS;
const ll N=1e5+21;
ll Ts,n,mod,ans,cnt;
ll vis[N],bin[N];
struct Bit_Array{
ll res; ll c[N<<1];
inline void update(ll x,ll w){
if(x<=0){ c[0]+=w; return ; }
for(;x<mod;x+=lbt(x)) c[x]+=w;
}
inline void del(ll x){
if(x<=0){ c[0]=0; return ;}
for(;x<mod;x+=lbt(x)) c[x]=0;
}
inline ll query(ll x){
res=0+c[0]*(x>=0);
for(;x>0;x&=(x-1)) res+=c[x]; return res;
}
inline ll ask(ll l,ll r){
return query(r)-query(l-1);
}
}bit;
signed main(){
File(exclaim);
Ts=read(); ll l,r,tmp=0;
for(int T=1;T<=Ts;T++){
n=read(),mod=read(),ans=0,cnt=0;
for(ll i=n;i>=1;i--){
tmp=i*i*i%mod,bit.update(tmp,1);
if(!vis[tmp]) vis[tmp]=1,bin[++cnt]=tmp;
if(i%mod==0) ans+=bit.ask(0,mod-1)*(i/mod);
else
{
l=(i*i+1)%mod,r=(i*i+i)%mod;
ans+=bit.ask(0,mod-1)*(i/mod);
if(l<=r) ans+=bit.ask(l,r);
else ans+=bit.ask(l,mod-1)+bit.ask(0,r);
}
}
for(int i=1;i<=cnt;i++) bit.del(bin[i]),vis[bin[i]]=0;
printf("Case %d: %lld\n",T,ans);
}
exit(0);
}
B. 简单的字符串
不会回文树,也不会 \(manacher\).
考场上就想到了这个题目的所有性质.
但是差一个 \(Border\) 理论 (膜拜这位 \(dalao\),讲得很清晰.)
考后看到题解中说到只会跳两三次,于是觉得暴跳 \(kmp\) 就行了.
虽然这样也能 \(A\),但实际上复杂度是假的,可以被卡成 \(O(n^3)\).
Accepted and Fake
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll int
#define ull unsigned ll
#define lf double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll res=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return cit?res:-res;
}
} using namespace BSS;
const ll N=6e3+21;
const ull P=131;
ll m,n,ans;
ll nxt[N][N];
ull pre[N],po[N],sp[N];
inline void PUT(ll l,ll r){ for(int i=l;i<=r;i++) cout<<sp[i]<<' '; puts(""); }
inline ull getsh(ll l,ll r){
return 1u*pre[r]-1u*pre[l-1]*po[r-l+1];
}
inline void getnxt(){
for(int st=1;st<=n;st++){
for(int i=st-1;i<=n;i++) nxt[st][i]=st-1;
for(int i=st+1,j=st-1;i<=n;i++){
while(j>st-1 and sp[i]!=sp[j+1]) j=nxt[st][j];
if(sp[i]==sp[j+1]) nxt[st][i]=++j;
else nxt[st][i]=st-1;
}
}
}
inline bool check(ll l,ll r){
ll mid=(l+r)>>1,nowmid=nxt[l][r];
if(getsh(l,mid)==getsh(mid+1,r)) return 1;
while(nowmid>mid) nowmid=nxt[l][nowmid];
while(nowmid>=l){
if(getsh(nowmid+1,mid)==getsh(mid+1,2*mid-nowmid)) return 1;
nowmid=nxt[l][nowmid];
}
return 0;
}
inline void Work(){
po[0]=1;
for(int i=1;i<=n;i++){
sp[i]=read(),pre[i]=pre[i-1]*P+sp[i];
po[i]=po[i-1]*P;
}
getnxt();
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j+=2){
if(nxt[i][j]<i) continue;
ans+=check(i,j);
}
}
}
signed main(){
File(s);
n=read(),Work();
printf("%d\n",ans),exit(0);
}
本以为这样就结束了,但是多亏 \(Yubai\) 来问我这题做法(因为貌似只有我一个人是用 \(kmp\) \(A\) 掉的).
实际上我们完全可以去掉一个 \(n\).
如果会了上面的理论,那么这就是个板子题了,现在正式进入题解.
我们发现答案其实就是由 \(A+B+B+A\) 构成的一个字符串(\(A,B\) 是字符串,\(B\)可以为空).
考虑得到 \(A\) :
然后我们在每个子区间 \([l,r]\) 分别处理出来 \(ta\) 的\(border\).
我们发现这个 \(border\) 长度可能大于 \(\dfrac{r-l+1}{2}\),考虑把 \(ta\) 缩小到一般长度以内.
只要学会上面的理论,这个东西还是很好说的.
设子串 \([l,r]\) 为 \(S\).
由于大于 \(\dfrac{len_s}{2}\) 的 \(Border\) 的长度为一个等差序列。
所以可以求出 \(S\) 的\(Max\ Border\),然后求出 \(Max\ Border\) 的 \(Max\ Border\) ,设两个 \(Max\ Border\) 分别为 \(M_1,M_2\).
公差就是 \(M_1\ - \ M_2\).
于是 \(A\) 便可以跳公差求出了.
考虑得到 \(B\) :
我们可以枚举中点,然后 \(Hash\) 得到中间 \(B\) 的部分.
这里建议结合 \(PPT\) 中给出的题解一起理解.
到此我们得到了 \(A\) 与 \(B\)..
我们得到了最长的 \(A\) 和最长的 \(B\) 之后,余下的我们分别进行判断.
这里都要判断,并不取决于 \(A\) 或 \(B\) 的相对大小而选择舍弃哪一个.
这里有 \(Yubai\) 的手模样例:1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 . 样例说明并不能舍弃某种判断.
至此本题结束,并膜掰.
\(upd: 2021.9.29\)
\(Yubai\) 又一次发现了新的问题所在,并给出了证明.
为了防止 \(Yubai\) 遗忘自己的智慧成果,这里决定盘一盘.
设 \(U=A+B,V=B+A\).
现有两个事实:
- \(A\) 是 \(U+V\) 的 \(\le mid\) 的最长 \(Border\).
- \(B\) 是 \(V+U\) 的 \(\le mid\) 的最长 \(Border\).
试证明:两个事实中至少一个成立.
我们可以只证明一个,另外一个同理.
如果 \(A\) 不是 \(U+V\) 的最长 \(Border\).
那么 \(U+V\) 的最长 \(Border\) 一定由 \(A+T\) 构成,其中 \(B=T+Z+T\).
现有推论:由于 \(A+T\) 是最长 \(Border\),那么 \(T\) 一定是 \(A\) 的一个整周期.
那么串 \(V+U\) 可以表示为 \(T+Z+T+x*T+x*T+T+Z+T\).
这时,如果 \(B\) 不是串 \(V+U\) 的最长 \(Border\),那么 \(Z\) 一定等于 \(y*T\).
这时与最初 \(A\) 不是 \(U+V\) 的最长 \(Border\) 的假设相悖.
至此证明结束,并再次膜掰.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll int
#define ull unsigned ll
#define lf double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll res=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return cit?res:-res;
}
} using namespace BSS;
const ll N=6e3+21;
const ull P=131;
ll m,n,ans;
ll nxt[N][N],bd[N][N];
ull pre[N],po[N],sp[N];
inline void PUT(ll l,ll r){ for(int i=l;i<=r;i++) cout<<sp[i]<<' '; puts(""); }
inline ull getsh(ll l,ll r){
return 1u*pre[r]-1u*pre[l-1]*po[r-l+1];
}
inline void getnxt(){
for(int st=1;st<=n;st++){
for(int i=st-1;i<=n;i++) nxt[st][i]=st-1;
for(int i=st+1,j=st-1;i<=n;i++){
while(j>st-1 and sp[i]!=sp[j+1]) j=nxt[st][j];
if(sp[i]==sp[j+1]) nxt[st][i]=++j;
else nxt[st][i]=st-1;
}
}
}
inline void getbord(){
for(int mid=1;mid<n;mid++){ // 枚举中点
ll lmt=min(mid,n-mid);
for(int l=1;l<=lmt;l++){
bd[mid][l]=bd[mid][l-1];
if(getsh(mid-l+1,mid)==getsh(mid+1,mid+l)) bd[mid][l]=l;
}
}
}
inline bool check(ll l,ll r){
ll mid=(l+r)>>1,nownxt=nxt[l][r];
if(nownxt>mid){
ll nxtnxt=nxt[l][nownxt],delta=nownxt-nxtnxt;
nownxt=nownxt-delta*((nownxt-mid)/mid);
if(nownxt>mid) nownxt=nxt[l][nownxt];
}
if(nownxt==mid) return 1;
if(getsh(nownxt+1,mid)==getsh(mid+1,2*mid-nownxt)) return 1;
ll len=r-mid,nowbd=bd[mid][len],br=mid+nowbd,bl=mid-nowbd+1;
if(l==bl and r==br) return 1;
if(getsh(l,bl-1)==getsh(br+1,r)) return 1;
return 0;
}
signed main(){
File(s);
n=read(),po[0]=1;
for(int i=1;i<=n;i++){
sp[i]=read();
pre[i]=pre[i-1]*P+sp[i],po[i]=po[i-1]*P;
}
getnxt(),getbord();
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j+=2){
if(nxt[i][j]<i) continue;
ans+=check(i,j);
}
}
printf("%d\n",ans),exit(0);
}
C. 环路
容易写出 \(dp\) 式子,发现写出来之后可以矩阵,每个矩阵主对角线的和就是最终答案.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define ll long long int
#define lf double
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define lbt(x) ((x)&(-(x)))
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(x))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[]()->ll{
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:(-w);
};
} using namespace BSS;
const ll N=105;
ll m,n,mod,ans;
struct Mat{
ll w[N][N];
friend Mat operator *(Mat a,Mat b){
Mat c;
for(ll i=1;i<=n;i++){
for(ll j=1;j<=n;j++){
c.w[i][j]=0;
for(ll k=1;k<=n;k++)
c.w[i][j]=(c.w[i][j]+a.w[i][k]*b.w[k][j]%mod)%mod;
}
}
return c;
}
friend Mat operator +(Mat a,Mat b){
for(ll i=1;i<=n;i++){
for(ll j=1;j<=n;j++)
a.w[i][j]=(a.w[i][j]+b.w[i][j])%mod;
}
return a;
}
}I,f,g,lst;
void love(ll k){
if(k==1) return f=g,lst=g,void();
love(k>>1); f=f*(lst+I),lst=lst*lst;
if(k&1) lst=lst*g,f=f+lst;
}
signed main(){
File(tour);
n=read(); char s[N];
for(ll i=1;i<=n;i++){
scanf("%s",s+1); I.w[i][i]=1;
for(ll j=1;j<=n;j++) g.w[i][j]=(s[j]=='Y');
}
m=read()-1,mod=read(),love(m);
for(ll i=1;i<=n;i++) ans=(ans+f.w[i][i])%mod;
printf("%lld\n",ans),exit(0);
}
D. 过河
读错题,直接死.
发现肯定要有一头猪和任何猪都有矛盾,这里将其命名为神猪.
第一步一定是先运送神猪过去.
对剩下的猪进行连边,发现如果是个二分图一定有解.
一开始或许认为不是二分图就不行,发现并不是这样的,因为还有一个东西叫做 小奇 .
对于形成三元环的猪,并不是不能在一起,但是与此同时小奇必须和所有已存在的三元环在一起.
考虑如何使用小奇,发现剩下的猪连边之后再删掉两个点,如果是个二分图那么有解.
于是 \(O(n)\) 枚举删掉的第一个猪,再 \(O(n)\) 在 \(dfs\) 序上判断是否合法就好了.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll long long
#define ull unsigned ll
#define lf long double
#define lbt(x) ((x)&(-(x)))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:-w;
}
} using namespace BSS;
const ll N=1e3+21,M=3e3+21;
ll m,n,ts,god,king,odds;
ll vis[N],dep[N],odd[N],head[N],cfo[N],cfe[N],fa[N],son[N],ans[N];
ll id[M][3];
struct I { ll u,v,nxt; } e[M<<1];
auto add=[](ll u,ll v)->void{
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
head[u]=ts;
};
auto PreWork=[]()->void{
king=0;
fill(vis,vis+n+5,0);
};
void dfs(ll u,ll dad){
fa[u]=dad,dep[u]=dep[dad]+1;
for(ll i=head[u];i;i=e[i].nxt){
if(e[i].v==dad) continue;
if(dep[e[i].v]){
if(dep[e[i].v]>=dep[u]) continue;
ll len=dep[u]-dep[e[i].v]+1;
if(len&1){
odd[u]++,odd[fa[e[i].v]]--,odds++;
cfo[u]++,cfo[son[e[i].v]]--;
}
else{
cfe[u]++,cfe[son[e[i].v]]--;
}
}
else{
son[u]=e[i].v,dfs(e[i].v,u);
if(cfe[e[i].v] and cfo[e[i].v]) ans[u]=-1;
cfe[u]+=cfe[e[i].v],cfo[u]+=cfo[e[i].v],odd[u]+=odd[e[i].v];
}
}
}
auto love=[](ll x)->bool{
if(x==god) return 0;
ll u,v; ts=0,odds=0;
for(ll i=1;i<=n;i++){
odd[i]=0,cfo[i]=0,cfe[i]=0,ans[i]=0;
head[i]=0,fa[i]=0,dep[i]=0,son[i]=0;
}
for(ll i=1;i<=m;i++){
u=0,v=0;
for(ll j=0;j<3;j++){
if(id[i][j]==god) continue;
u ? v=id[i][j] : u=id[i][j];
}
if(u==x or v==x) continue;
add(u,v),add(v,u);
}
for(ll i=1;i<=n;i++){
if(i==x or i==god) continue;
if(!dep[i]) dfs(i,0);
}
for(ll i=1;i<=n;i++){
if(i==x or i==god or ans[i]<0) continue;
if(odd[i]==odds) return 1;
}
return 0;
};
auto Work=[]()->void{
n=read(),m=read(),PreWork(); ll flag=0;
for(ll i=1;i<=m;i++){
for(ll j=0;j<3;j++) vis[id[i][j]=read()]++;
}
for(ll i=1;i<=n;i++)flag+=(vis[i]==m);
if(!flag) { puts("no"); return ; }
if(flag>=2) { puts("yes"); return ; }
for(ll i=1;i<=n;i++) if(vis[i]==m) god=i;
for(ll i=1;i<=n;i++) if(love(i)){ puts("yes"); return ; }
puts("no"); return ;
};
signed main(){
File(river);
for(int Ts=read();Ts;Ts--) Work();
exit(0);
}