noip模拟25
A.random
数学推理.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x);
#define Copy(x,y) memset(y,x,sizeof x);
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll mod=998244353;
ll m,n,ts;
inline ll ksm(ll a,ll b,ll c)
{
a%=c; ll temp=1;
while(b)
{
if(b&1) temp=(temp*a)%c;
b>>=1; a=(a*a)%c;
}
return temp%c;
}
signed main()
{
ts=read(); ll tmp=ksm(9,mod-2,mod);
while(ts--)
{
n=read();
ll temp=n%mod; temp=(temp*temp)%mod;
temp=(temp+mod-1)%mod;
temp=(temp*tmp)%mod;
write(temp);
}
return 0;
}
B.string
选择将所有模式串正着和倒着插入两棵Trie树..
我们发现如果一段字符串可以匹配文本串,那么这个字符串的任意一段前缀/后缀都可以进行匹配..
所以我们可以维护Trie树上的方案前缀和..
于是可以枚举断点,二分可以匹配的最长后缀/前缀,统计答案即可..
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll unsigned long long int
#define re register long long int
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x);
#define Copy(x,y) memset(y,x,sizeof x);
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return ss;
}
} using namespace BSS;
const ll N=5e5+50;
const ll p=131;
ll m,n,ls,tsp,tss;
char s[N],s2[N*2];
ll trp[N*2][27],trs[N*2][27];
ll pre[N*60],suf[N*60],spre[N*2],ssuf[N*2];
ll down[N+50];
unordered_map<ll,ll> mp1,mp2;
void insert_pre()
{
ll p=0; ll len=strlen(s2+1);
for(ll i=1;i<=len;i++)
{
if(!trp[p][s2[i]-'a'+1])
trp[p][s2[i]-'a'+1]=++tsp;
p=trp[p][s2[i]-'a'+1];
pre[p]++;
}
return ;
}
void insert_suf()
{
ll p=0; ll len=strlen(s2+1);
for(ll i=1;i<=len;i++)
{
if(!trs[p][s2[i]-'a'+1])
trs[p][s2[i]-'a'+1]=++tss;
p=trs[p][s2[i]-'a'+1];
suf[p]++;
}
return ;
}
void dfs_pre(ll now,ll hash,ll val)
{
// printf("Pre:%llu %llu\n",hash,val);
mp1[hash]=val;
for(ll i=1;i<=26;i++)
{
if(trp[now][i])
{
dfs_pre(trp[now][i],hash*p+i,val+pre[trp[now][i]]);
}
}
return ;
}
void dfs_suf(ll now,ll hash,ll val)
{
// printf("Suf:%llu %llu\n",hash,val);
mp2[hash]=val;
for(ll i=1;i<=26;i++)
{
if(trs[now][i])
{
dfs_suf(trs[now][i],hash*p+i,val+suf[trs[now][i]]);
}
}
return ;
}
inline ll check_front(ll now)
{
ll l=1,r=now,mid; ll temp;
while(l<=r)
{
mid=(l+r)>>1;
temp=ssuf[mid]-ssuf[now+1]*down[now-mid+1];
if(mp2.find(temp)!=mp2.end()) r=mid-1;
else l=mid+1;
}
temp=ssuf[l]-ssuf[now+1]*down[now-l+1];
return mp2[temp];
}
inline ll check_back(ll now)
{
ll l=now,r=ls,mid; ll temp;
// cout<<"now:"<<now<<endl;
while(l<=r)
{
mid=(l+r)>>1;
// cout<<"L:"<<l<<" R:"<<r<<" Mid:"<<mid<<" ";
temp=spre[mid]-spre[now-1]*down[mid-now+1];
// cout<<"temp:"<<temp<<endl;
// for(re i=now;i<=mid;i++) cout<<s[i]<<" ";
// cout<<endl;
if(mp1.find(temp)!=mp1.end()) l=mid+1;
else r=mid-1;
}
// cout<<"l:"<<l<<" r:"<<r<<" mid:"<<mid<<endl;
temp=spre[r]-spre[now-1]*down[r-now+1];
return mp1[temp];
}
signed main()
{
scanf("%s",s+1);
ls=strlen(s+1);
n=read();
for(ll i=1;i<=n;i++)
{
scanf("%s",s2+1);
insert_pre();
reverse(s2+1,s2+1+strlen(s2+1));
insert_suf();
}
for(ll i=1;i<=26;i++)
{
if(trp[0][i]) dfs_pre(trp[0][i],i,pre[trp[0][i]]);
if(trs[0][i]) dfs_suf(trs[0][i],i,suf[trs[0][i]]);
}
down[0]=1;
for(ll i=1;i<=N-50;i++)
down[i]=down[i-1]*p;
for(ll i=1;i<=ls;i++)
spre[i]=spre[i-1]*p+s[i]-'a'+1;
for(ll i=ls;i>=1;i--)
ssuf[i]=ssuf[i+1]*p+s[i]-'a'+1;
ll temp1,temp2,ans=0;
for(ll i=1;i<=ls-1;i++)
{
temp1=check_front(i);
temp2=check_back(i+1);
ans+=temp1*temp2;
}
printf("%lld",ans);
return 0;
}
C.queen
考场上想着要什么正难取反,但这其实是一个极其分类讨论的题目..
大家都写的是直接写式子,而我选择了数形结合使用组合数目..
统计不同情况的前缀和,发现是等差数列,于是选择使用组合数..
首先画出矩阵,然后填数+切割,计算前缀即可,组合数优化..
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x);
#define Copy(x,y) memset(y,x,sizeof x);
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll mod=3e5+7;
ll m,n,num,ts;
ll ny[mod+50],add[mod+50];
inline ll ksm(ll a,ll b,ll c)
{
a%=c; ll temp=1;
while(b)
{
if(b&1) temp=(temp*a)%c;
b>>=1; a=(a*a)%c;
}
return temp%c;
}
inline ll C(ll a,ll b)
{
if(a>b) return 0;
return ((add[b]*ny[a]%mod)*ny[b-a])%mod;
}
ll lucas(ll a,ll b)
{
if(a==0) return 1;
if(b==0 or a==b) return 1;
return (C(a%mod,b%mod)*lucas(a/mod,b/mod))%mod;
}
void Prepare()
{
add[0]=1;
for(re i=1;i<=mod;i++)
add[i]=(add[i-1]*i)%mod,ny[i]=ksm(add[i],mod-2,mod);
return ;
}
ll Work_Line(ll n,ll m,ll num)
{
if(num>n and num>m) return 0;
if(n<m) swap(n,m);
ll temp=(lucas(num,n)*(m%mod))%mod;
if(num>m) return temp;
ll ans=(lucas(num,m)*(n%mod))%mod;
ans=(ans+temp)%mod;
temp=(lucas(num+1,m)*2)%mod;
temp=(temp+(((n-m+1)%mod)*lucas(num,m))%mod)%mod;
temp=(temp*2)%mod;
ans=(ans+temp)%mod;
return ans;
}
ll Work_Square(ll n,ll m)
{
ll ans=(lucas(3,m+1)+lucas(3,m))%mod;
ll temp=(n-m)%mod; temp=(temp*lucas(2,m))%mod;
ans=(ans+temp)%mod; ans=ans%mod;
return ans;
}
ll Work_Square23(ll n,ll m)
{
if(n<3) return 0;
if(m<2) return 0;
ll ans=0,temp;
if(n>=2*m)
{
temp=(n-2*m)%mod; temp=(temp*lucas(2,m))%mod;
temp=(temp+(lucas(3,m)*2))%mod;
temp=(temp+(lucas(3,m+1)*2)%mod)%mod;
ans=(ans+temp)%mod;
}
else
{
ll t=n>>1;
if(n&1)
{
temp=(m-t)%mod; temp=(temp*(t%mod))%mod;
temp=(temp+lucas(2,t))%mod;
ll tmp=(m-t)%mod; tmp=(tmp<<1)%mod;
tmp=(tmp*lucas(2,t))%mod;
temp=(temp+tmp)%mod;
temp=(temp+(lucas(3,t)*2)%mod)%mod;
temp=(temp+(lucas(3,t+1)*2)%mod)%mod;
ans=(ans+temp)%mod;
}
else
{
temp=(m-t)%mod; temp=(temp*lucas(2,t))%mod;
temp=(temp+lucas(3,t))%mod; temp=(temp+lucas(3,t+1))%mod;
temp=(temp<<1)%mod;
ans=(ans+temp)%mod;
}
}
return (ans*2)%mod;
}
ll Work_Odd(ll n,ll m)
{
if(m==1) return 0;
ll t=(m>>1),ans=0;
if(m&1)
{
ans=((lucas(3,t+1)%mod)<<3)%mod;
ll temp=(n-m+1)%mod;
temp=(temp*((lucas(2,t)+lucas(2,t+1)%mod)))%mod;
ans=(ans+temp)%mod;
ans=(ans-(lucas(2,t)*2%mod)+mod*mod)%mod;
}
else
{
ans=(lucas(3,t+1)+lucas(3,t))%mod;
ans=(ans<<2)%mod;
ll temp=(lucas(2,t)*2)%mod;
temp=(((n-m)%mod)*temp)%mod;
ans=(ans+temp)%mod;
}
return ans;
}
signed main()
{
Prepare();
ts=read();
ll m,n,num;
while(ts--)
{
n=read(); m=read(); num=read();
if(n<m) swap(n,m); // 使得 n > m
if(num==1)
{
write(((n%mod)*(m%mod))%mod);
continue;
}
if(num==2 or num>5)
{
ll ans=Work_Line(n,m,num);
write(ans);
continue;
}
if(num==3)
{
ll ans=(Work_Square(n,m)<<2)%mod;
ans=(ans+Work_Square23(n,m))%mod;
ans=(ans+Work_Square23(m,n))%mod;
ans=(ans+Work_Line(n,m,num))%mod;
write(ans);
continue;
}
if(num==4)
{
ll ans=(Work_Odd(n,m)*5)%mod;
ans=(ans+Work_Square(n,m))%mod;
ans=(ans+Work_Square23(m,n))%mod;
ans=(ans+Work_Square23(n,m))%mod;
ans=(ans+Work_Line(n,m,num))%mod;
write(ans);
continue;
}
if(num==5)
{
ll ans=(Work_Odd(n,m)*2)%mod;
ans=(ans+Work_Line(n,m,num))%mod;
write(ans);
continue;
}
}
}