正题
第一题:
答案就是1^n,证明考虑最短路路径上的点一定是n的子集,除1外,否则会加上更多的东西。
那么答案怎么算都是1^n。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int T;
int n;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("%d\n",1^n);
}
}
第二题:最短路2
做法就是在对于每一个点向外跑SPFA记录两个权值,一个是路径上除首尾外的最大值,一个是路径长度,然后更新的时候判断一下路径上的编号最大值变小即可。
这样的时间复杂度是玄学的,不过可以通过此题,标准做法考虑把最短路图跑出来,然后就相当于在DAG上DP了。
可以做到严格复杂度。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int N=1010,M=2010;
const long long data=1000000000000000ll,mod=998244353;
int T,n,m;
struct edge{
int y,next,c;
}s[M<<1];
int first[N],len;
long long dis[N][N];
bool tf[N];
int mmax[N][N];
queue<int> list;
void ins(int x,int y,int c){
s[++len]=(edge){y,first[x],c};first[x]=len;
s[++len]=(edge){x,first[y],c};first[y]=len;
}
void SPFA(int begin){
for(int i=1;i<=n;i++) dis[begin][i]=data;dis[begin][begin]=0;mmax[begin][begin]=0;
for(int i=first[begin];i!=0;i=s[i].next) dis[begin][s[i].y]=min(dis[begin][s[i].y],s[i].c*1ll),mmax[begin][s[i].y]=0,list.push(s[i].y),tf[s[i].y]=true;
while(!list.empty()){
int x=list.front();list.pop();tf[x]=false;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(dis[begin][y]>dis[begin][x]+s[i].c || dis[begin][y]==dis[begin][x]+s[i].c && mmax[begin][y]>max(mmax[begin][x],x)){
dis[begin][y]=dis[begin][x]+s[i].c;
mmax[begin][y]=max(mmax[begin][x],x);
if(!tf[y]) tf[y]=true,list.push(y);
}
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) first[i]=0;len=0;
int x,y,c;
for(int i=1;i<=m;i++) scanf("%d %d %d",&x,&y,&c),ins(x,y,c);
for(int i=1;i<=n;i++) SPFA(i);
long long tot=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) tot+=mmax[i][j];
}
printf("%I64d\n",tot%mod);
}
}
Close
第三题:
这个东西很明显就可以想到gcd,可以写成:。
证明可以考虑若一边有平方因子,那么显然为0,否则,可以换成的形式,因为
,所以就有上米昂那个东西。
然后莫比乌斯反演就可以得到:
做完了,直接枚举T即可,后面的东西可以对于每一个n,m预处理,也可以一开始用空间存储下来,所以:
时间复杂度为,任君选择。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int N=1e6;
int T,n,m;
int mu[N+10],p[N+10];
long long a[N+10],sum1[N+10],sum2[N+10];
long long tot=0;
bool vis[N+10];
void prepare(){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!vis[i]) {p[++p[0]]=i;mu[i]=-1;}
for(int j=1;j<=p[0] && i*p[j]<=N;j++){
vis[i*p[j]]=true;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++) p[i]=mu[i];
for(int i=1;i<=N;i++)
for(int j=1;j<=N/i;j++)
a[i*j]+=mu[i]*p[j];
}
int main(){
scanf("%d",&T);
prepare();
while(T--){
scanf("%d %d",&n,&m);
if(m<n) swap(n,m);tot=0;
for(int i=1;i<=n;i++){
sum1[i]=0;
for(int j=1;j<=n/i;j++)
sum1[i]+=mu[i*j];
}
for(int i=1;i<=m;i++){
sum2[i]=0;
for(int j=1;j<=m/i;j++)
sum2[i]+=mu[i*j];
}
for(int i=1;i<=n;i++) tot+=a[i]*sum1[i]*sum2[i];
printf("%I64d\n",tot);
}
}
Close
第四题:
求偏导,相当于把其他变量都看成系数,然后可以化成一个函数,导数就是
,所以相当于求
的系数。
这个很简单,把这个函数关系看成一棵树,然后对于每一个节点维护一棵子树的权值和。更新log
求答案的时候考虑只有乘号的时候另一棵子树才会产生影响,否则没有影响。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int M=1<<20;
int m,q;
char s[M];
long long sum[M];
const long long mod=998244353;
void change(int x,int y){
x+=(1<<(m-1))-1;
sum[x]=y;
x/=2;
while(x){
if(s[x]=='1') sum[x]=sum[x<<1]*sum[x<<1|1]%mod;
else sum[x]=(sum[x<<1]+sum[x<<1|1])%mod;
x/=2;
}
}
void solve(int x){
long long ans=1;
x+=(1<<(m-1))-1;
int last=x;x/=2;
while(x){
if(s[x]=='1') (ans*=sum[last^1])%=mod;
last=x;x/=2;
}
printf("%lld\n",ans);
}
int main(){
scanf("%d %d",&m,&q);
scanf("%s",s+1);
int op,x,y;
for(int i=1;i<=1<<(m-1);i++) change(i,i);
while(q--){
scanf("%d %d",&op,&x);
if(op==1) {
scanf("%d",&y);
change(x,y);
}
else solve(x);
}
}
第五题:
这是道好题
可以找规律或者观察式子发现,对于连续的三个数,最后一个数不能是严格最小数。
考虑从小到大插入,相同的数一起插入。显然一个不合法的序列不可能通过加更大的数数来变得合法。
一开始我们先把最小的相同几个数加进去。
如果前面有一个这样的对子,那么在i前面和i+1的前面都不能放数字,否则就不合法。
两个相同的数显然不可以放在一起(除了在最后),否则不合法。
那么我们用来记录有x个空这样的对子的方案数。
转移显然。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const long long mod=998244353;
const int N=1010;
long long f[2][N],fac[N],inv[N];
long long ans;
int a[N];
int T,n,tot;
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;
t/=2;
}
return tot;
}
long long C(int x,int y){
return fac[x]*inv[x-y]%mod*inv[y]%mod;
}
int main(){
scanf("%d",&T);
fac[0]=1;for(int i=1;i<=1000;i++) fac[i]=fac[i-1]*i%mod;
inv[1000]=ksm(fac[1000],mod-2);for(int i=999;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int now=1,in=0,op=0;
long long temp=1;
memset(f[op],0,sizeof(f[op]));
while(now<n && a[now]==a[now+1]) now++;
f[op][0]=1;in=now;now++;
(temp*=fac[in])%=mod;
while(now<=n){
tot=1;op^=1;
while(now<n && a[now]==a[now+1]) now++,tot++;
memset(f[op],0,sizeof(f[op]));
for(int i=0;i<=tot;i++)
for(int j=0;i+2*j<=in;j++)
f[op][i+j]+=f[op^1][j]*C(in-2*j,i)%mod;
for(int i=0;i<=in;i++) f[op][i]%=mod;
in=now;now++;
(temp*=fac[tot])%=mod;
}
ans=0;
for(int i=0;i<=n;i++) ans+=f[op][i];
printf("%lld\n",ans%mod*temp%mod);
}
}
第六题:不会

797

被折叠的 条评论
为什么被折叠?



