2021.9.28考试总结[NOIP模拟64]
T1 三元组
发现确定\(b,c\)的情况下,\(a\)的值域是连续的。确定\(b\)后\(a+b\)的取值是\([1+b,b+b]\)。树状数组维护对每个\(b\)可行的\(c\)。
注意取模后取值可能跨越多次值域。
\(code:\)
T1
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=100010;
int T,n,k,dlt,ans,cnt[50];
namespace BIT{
int c[NN];
void insert(int pos,int x){
if(!pos) return c[0]+=x,void();
while(pos<=max(n,k)){
c[pos]+=x;
pos+=pos&-pos;
}
}
int query(int pos){
int res=c[0];
if(pos<0) return 0;
while(pos){
res+=c[pos];
pos-=pos&-pos;
}
return res;
}
} using namespace BIT;
signed main(){
freopen("exclaim.in","r",stdin);
freopen("exclaim.out","w",stdout);
T=read();
for(int t=1;t<=T;t++){
n=read(); k=read();
ans=0; memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) insert(i*i*i%k,1);
for(int i=1;i<=n;i++){
int l=(1+i*i)%k,r=(i+i*i)%k;
if((i%k)==0) ans+=query(k-1)*(i/k);
else if(l<=r) ans+=query(r)-query(l-1)+query(k-1)*(i/k);
else ans+=query(k-1)-query(l-1)+query(r)+query(k-1)*(i/k);
insert(i*i*i%k,-1);
}
printf("Case %lld: %lld\n",t,ans);
}
return 0;
}
T2 简单的字符串
\(n^3\)加剪枝。
\(code:\)
T2
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
namespace IO{
int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const ULL base=6121;
const int NN=5010;
int n,ans,col[NN],sum[NN];
ULL pw[NN],has[NN];
inline ULL get(int l,int r){ return l>r?0:has[r]-has[l-1]*pw[r-l+1]; }
signed main(){
freopen("s.in","r",stdin);
freopen("s.out","w",stdout);
n=read(); pw[0]=1;
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(col[i]=read());
for(int i=1;i<NN;i++) pw[i]=pw[i-1]*base;
for(int i=1;i<=n;i++)
has[i]=has[i-1]*base+(ULL)col[i];
for(int r=2;r<=n;r++) for(int l=1;l<r;l++) if((r-l)&1){
int mid=l+r>>1;
if(sum[mid]-sum[l-1]!=sum[r]-sum[mid]) continue;
ULL s2=get(mid+1,r);
for(int b=l;b<=mid;b++){
ULL s1=get(b,mid)*pw[b-l]+get(l,b-1);
if(s1==s2){ ++ans; break; }
}
}
write(ans,'\n');
return 0;
}
T3 环路
邻接矩阵的连续幂次和。
由于只需求主对角线的和,开两倍的矩阵可以统计。
\(code:\)
T3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=110,MM=NN<<1;
int n,m,k,ans;
char O[NN];
namespace matrix{
int ext;
struct mat{
int s[MM][MM];
void clr(){ memset(s,0,sizeof(s)); }
void pre(){ clr(); for(int i=1;i<=ext;i++) s[i][i]=1; }
mat operator*(const mat& a)const{
mat res; res.clr();
for(int i=1;i<=ext;i++)
for(int k=1;k<=ext;k++)
for(int j=1;j<=ext;j++)
(res.s[i][j]+=1ll*s[i][k]*a.s[k][j]%m)%=m;
return res;
}
}to;
void qpow(int b){
mat res; res.pre();
while(b){
if(b&1) res=res*to;
to=to*to;
b>>=1;
}
to=res;
}
} using namespace matrix;
signed main(){
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
n=read(); ext=n<<1;
for(int i=1;i<=n;i++){
scanf("%s",O+1);
for(int j=1;j<=n;j++)
to.s[i][j]=(O[j]=='Y');
to.s[i][n+i]=to.s[n+i][n+i]=1;
}
k=read(); m=read();
qpow(k);
for(int i=1;i<=n;i++)
(ans+=to.s[i][i+n])%=m;
(ans+=m-n)%=m;
write(ans,'\n');
return 0;
}
T4 过河
发现方案肯定是先运所有关系的交集(猪王),然后每个关系运一个,之后把猪王运回来,再把其他猪运过去,最后把猪王运回去。
于是转化为二分图问题。发现猪王来回可以带两头猪,问题转化为删两个点有无方案令图为二分图。
枚举其中一个点,另一个点合法当且仅当它在所有奇环上,且它儿子的子树内不同时存在奇环与偶环上的边连向它的祖先链。建出搜索树,树上差分奇环个数即可。
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=1010,MM=3010;
int t,n,m,ban,root,in[NN];
int idx,to[MM<<1],nex[MM<<1],head[NN],id[MM<<1];
bool Continue;
map<pair<int,int>,bool>mp;
struct relation{
int a,b,c;
}p[MM];
inline void add(int a,int b,int c){
if(mp[make_pair(a,b)]) return;
mp[make_pair(a,b)]=mp[make_pair(b,a)]=1;
to[++idx]=b; nex[idx]=head[a]; head[a]=idx; id[idx]=c;
to[++idx]=a; nex[idx]=head[b]; head[b]=idx; id[idx]=c;
}
int sum,fa[NN],odd[NN],evo[NN],eve[NN],dep[NN],son[NN];
bool vis[MM],able[NN];
void clear(){
sum=0;
memset(odd,0,sizeof(odd));
memset(evo,0,sizeof(evo));
memset(eve,0,sizeof(eve));
memset(dep,0,sizeof(dep));
memset(vis,0,sizeof(vis));
memset(able,0,sizeof(able));
}
void dfs(int s,int f,int d){
dep[s]=d; fa[s]=f; able[s]=1;
for(int i=head[s];i;i=nex[i]) if(!vis[id[i]]){
int v=to[i]; vis[id[i]]=1;
if(ban==v||root==v) continue;
if(dep[v]){
int tmp=dep[s]-dep[v]+1;
if(tmp&1){
++sum; ++odd[s]; --odd[fa[v]];
++evo[s]; --evo[son[v]];
}
else ++eve[s], --eve[son[v]];
continue;
}
son[s]=v;
dfs(v,s,d+1);
if(eve[v]&&evo[v]) able[s]=0;
odd[s]+=odd[v];
eve[s]+=eve[v];
evo[s]+=evo[v];
}
}
signed main(){
freopen("river.in","r",stdin);
freopen("river.out","w",stdout);
t=read();
while(t--){
n=read(); m=read(); Continue=1; idx=root=0;
memset(head,0,sizeof(head));
memset(in,0,sizeof(in));
mp.clear();
for(int a,b,c,i=1;i<=m;i++){
a=read(); b=read(); c=read();
p[i].a=a; p[i].b=b; p[i].c=c;
++in[a]; ++in[b]; ++in[c];
if(in[a]==m||in[b]==m||in[c]==m) Continue=0;
}
if(n<4){ puts("no"); continue; }
if(Continue){ puts("no"); continue; }
for(int i=1;i<=n;i++) if(in[i]==m)
if(!root) root=i;
else Continue=1;
if(Continue){ puts("yes"); continue; }
for(int i=1;i<=m;i++)
if(p[i].a==root) add(p[i].b,p[i].c,i);
else if(p[i].b==root) add(p[i].a,p[i].c,i);
else if(p[i].c==root) add(p[i].a,p[i].b,i);
for(int k=1;k<=n;k++) if(root!=k){
ban=k; clear();
for(int i=1;i<=n;i++) if(i!=root&&i!=ban&&!dep[i])
dfs(i,0,1);
for(int i=1;i<=n;i++) if(i!=root&&i!=ban)
if(able[i]&&odd[i]==sum) Continue=1;
}
puts(Continue?"yes":"no");
}
return 0;
}