1001 Mod, Or and Everything
两个for循环打表,总结结果
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll pow1(ll a,ll b){ll r=1;while(b){if(b&1)r=r*a;a=a*a;b>>=1;}return r;}
ll t,n;
int main()
{
// for(int i=1;i<=1000;i++){
// int x=0;
// for(int j=1;j<=i;j++)
// x|=i%j;
// cout<<x<<' ';
// }
cin>>t;
while(t--){
cin>>n;
ll k=2,cnt=0;
while(k<n) ++cnt,k*=2;
cout<<pow1(2,cnt)-1<<'\n';
}
}
1005 Minimum spanning tree
当一个数为质数时与2相连,贡献是这个数的两倍,为合数时与他的其中一个质因子相连,贡献为这个数本身,预处理出质数和结果
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e6+5;
const int D=1e7+5,PD=7e5+5;
ll cnt,p[PD],t,n;
ll a[D];
bool np[D];
int main()
{
//欧拉素数筛
for(int i=2;i<D;i++){
if(!np[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&p[j]*i<D;j++){
np[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
a[2]=0;
int k=2;
for(ll i=3;i<D;i++){
if(p[k]==i){
a[i]=a[i-1]+i*2;
k++;
}
else
a[i]=a[i-1]+i;
}
cin>>t;
while(t--){
cin>>n;
cout<<a[n]<<'\n';
}
}
1006 Xor sum
前置知识
可以将打印的注释和IOS去掉,观察变化
#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
#define dep(i,x,y) for(auto i=(x);i>=(y);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;
int t,n,k;
int a[N],p[N*30][2],mx[N*30];//p是字典树,mx是记录当前结点的最右边的位置
int main()
{
IOS;
cin>>t;while(t--){
cin>>n>>k;
rep(i,1,n) cin>>a[i],a[i]^=a[i-1];
int l=-1,r=n,cnt=1; //l为左边界,为右边界
mx[1]=-1;
p[1][0]=p[1][1]=0;
rep(i,0,n){
int x=1,lp=-1; //lp表示左边界
dep(j,29,0){
int w=(a[i]>>j)&1;
if(!((k>>j)&1)){
if(p[x][w^1]) lp=max(mx[p[x][w^1]],lp);//k的这位是0,那么w这位要异或与他本身的不一样的才能使结果大于k
x=p[x][w];
}
else x=p[x][w^1];//k的这位是1,则一定要异或不一样的
if(!x) break; //x==0时,说明没有这个数
}
if(x) lp=max(mx[x],lp);
if(lp>=0&&i-lp<r-l) r=i,l=lp;
x=1;
// cout<<"a["<<i<<"]="<<a[i]<<'\n';
dep(j,29,0){
int w=(a[i]>>j)&1;
if(!p[x][w]){
p[x][w]=++cnt;
p[cnt][0]=p[cnt][1]=0;
mx[cnt]=-1;
}
x=p[x][w];
mx[x]=i;
// cout<<"p0"<<" ";for(int i=1;i<=40;i++) printf("%2d",p[i][0]),cout<<" ";cout<<'\n';
// cout<<"p1"<<" ";for(int i=1;i<=40;i++) printf("%2d",p[i][1]),cout<<" ";cout<<"\n";
// cout<<"mx"<<" ";for(int i=1;i<=40;i++) printf("%2d",mx[i]),cout<<" ";cout<<"\n\n";
}
}
l>=0?cout<<l+1<<" "<<r<<'\n':cout<<-1<<'\n';
}
}
1007 Pass!
f(t)=(n-2)f(t-1)+(n-1)f(t-2)
特征方程 x^2-(n-2)x-(n-1)=0
特征根 x1=n-1 x2=-1
特征方程f(t)=A(n-1)^ t + B(-1)^t
已知 f(0)=1,f(1)=0
则A+B=1,A(n-1)-B=0,
A=1/n,B=(n-1)/n
f(t)=((n-1)t+(n-1)(-1)t)/n
分奇偶
f(t)=((n-1)t-(n-1))/n (n为奇)
f(t)=((n-1)t+(n-1))/n (n为偶)
移项
z1=nf(t)+(n-1)=(n-1)t (n为奇)
z2=nf(t)-(n-1)=(n-1)t (n为偶)
已知y=n-1,z如上,p=998244353,求t
用BSGS算法
yt≡z(mod)p
当gcd(y,p)=1时
设 t=ax-b则 y(ax-b)≡z(mod p) (a,b∈[0,√p))
y ax≡z*yb (mod p)
inv(z)*y ax≡yb (mod p)
暴力枚举 b,用unordered_map存<yb,b>
暴力枚举a, 找到map[inv(z)*y ax]对应的数
#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
#define dep(i,x,y) for(auto i=(x);i>=(y);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e6+5;
const ll MOD=998244353;
ll pow2(ll a,ll b,ll mod){ll r=1;while(b){if(b&1)r=r*a%mod;a=a*a%mod;b>>=1;}return r%mod;}
void exgcd(ll a, ll b, ll &d, ll &x, ll &y){if(!b) { d = a; x = 1; y = 0;}else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }}
ll inv(ll a){ll d, x, y;exgcd(a, MOD, d, x, y);return d == 1 ? (x + MOD) % MOD : -1;}
ll p,y,z1,z2,t,n,k;
unordered_map<ll,ll>mp;
int main()
{
scanf("%lld",&t);
while(t--){
mp.clear();
scanf("%lld%lld",&n,&k);
if(k==0){cout<<1<<'\n';continue;}
if(k==1){cout<<0<<'\n';continue;}
p=MOD;
y=n-1;
z1=inv((k*n+n-1)%p);//奇数
z2=inv((k*n-n+1)%p);//偶数
ll b=(ll)sqrt(p);
ll x=y,yy=1;
rep(i,1,b){
mp[x]=i;
x=x*y%p;
}
x=pow2(y,b,p);
rep(i,0,b){
ll p1=yy;
yy=yy*x%p;
if(mp[p1*z1%p]){
ll x=i*b-mp[p1*z1%p];
if(x&1){
cout<<x<<'\n';
goto kkk;
}
}
if(mp[p1*z2%p]){
ll x=i*b-mp[p1*z2%p];
if(!(x&1)){
cout<<x<<'\n';
goto kkk;
}
}
}
cout<<-1<<'\n';
kkk:continue;
}
}
1008 Maximal submatrix
思路是将矩阵的信息进行转换,转换成每个点的数字表示继续往下不递减的序列的长度
1 2 1 4 ------ 4 2 3 1
2 3 2 3 ------ 3 1 2 1
3 2 3 2 ------ 2 2 1 1
4 2 1 1 ------ 1 1 1 1
转化完成后,就是计算每一行的所有区间的最小值乘以区间的长度的值,取最大就是答案,暴力复杂度O(n3),我们可以用单调栈来计算这个数是最小的时候的左边界和右边界,复杂度将为O(n2)
#include<bits/stdc++.h>
#define FORz(i,t,n) for(int i=t;i<=n;i++)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;
int t,n,m,a[2005][2005],c[2005],b[2005][2005],l[2005],r[2005],zhan[2005],top;
int main()
{
IOS;
cin>>t;
while(t--){
cin>>n>>m;
FORz(i,1,n) FORz(j,1,m) cin>>a[i][j];
FORz(j,1,m) {
int l=1,i;
++c[j];
for(i=1;i<=n;i++){
if(a[i][j]<a[i-1][j]){
++c[j];
b[i][j]=c[j];
}
else b[i][j]=c[j];
}
}
for(int j=1;j<=m;j++){
int cnt=1;
a[n][j]=1;
for(int i=n-1;i>=1;i--){
if(b[i][j]!=b[i+1][j]) cnt=0;
++cnt;
a[i][j]=cnt;
}
}
ll ans=m;
//计算每一行的区间最小值乘以区间长度的最大值
for(int p=1;p<=n;p++){
for(int i=1;i<=m;++i) c[i]=a[p][i];
for(int i=1;i<=m;++i)l[i]=0,r[i]=m+1;
top=0;
for(int i=1;i<=m;++i)
{
while(top&&c[zhan[top]]>=c[i])r[zhan[top--]]=i;
l[i]=zhan[top];
zhan[++top]=i;
}
for(int i=1;i<=m;++i) ans=max(ans,(ll)(r[i]-l[i]-1)*c[i]);
}
cout<<ans<<'\n';
}
}
1009 KD-Graph
思路是先将边长排序,当边长相同的时候进行连边,连在一起的分为1组,各组之间没连边的边都严格大于D,边不同的时候判断组数是否为k,为k则成功。
#include<bits/stdc++.h>
#define FORz(i,t,n) for(int i=t;i<=n;i++)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;
int a[N],n,m,k,t;
struct ty{
int u,v,c;
bool operator <(ty kk){
return c<kk.c;
}
}e[N*5];
int find(int x){return a[x]==x?x:a[x]=find(a[x]);}
void merge(int x,int y){a[find(y)]=a[find(x)];}
int main()
{
IOS;
cin>>t;
while(t--){
cin>>n>>m>>k;
int v,u,c;
FORz(i,1,m) cin>>e[i].u>>e[i].v>>e[i].c;
sort(e+1,e+m+1);
FORz(i,1,n) a[i]=i;
int b=n,ans=0; //b是组数,刚开始n表示每个点都在各自的组
FORz(i,1,m){
if(e[i].c!=e[i-1].c) {if(b==k) goto kkk;} //路径不一样时组数已经为k,这时已经满足所有组之间的路径大于ans
if(find(e[i].u)==find(e[i].v)) continue;//两个点已经相连说明两个点之间已经有更小的路径,不用更新ans
b--;merge(e[i].u,e[i].v);ans=e[i].c;//合并的点放入同一组,这时组中的路径最大值ans要更新
}
b==k?cout<<ans<<'\n':cout<<-1<<'\n';
continue;
kkk:cout<<ans<<'\n';
}
}
1010 zoto
思路是用 num维护x轴上l到r上的y坐标,对sum进行分块,统计每块内num>0的个数
#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;
int t,n,m,a[N],num[N],sum[N],ans[N],k=315;
struct ty{
int l,r,L,R,id;
//按组排序,如果按l的大小排序会超时,因为r的大小会使得while循环多次运行
bool operator <(ty kk){
if(l/k==kk.l/k) return r<kk.r;
return l/k<kk.l/k;
}
}b[N];
add(int x){
if(++num[x]==1) sum[x/k]++;
}
dec(int x){
if(--num[x]==0) sum[x/k]--;
}
int calc(int x,int y){
int cnt=0;
for(int i=x;i<min((x/k+1)*k,y+1);i++) cnt+=(num[i]>0);
for(int i=x/k+1;i<y/k;i++) cnt+=sum[i];
for(int i=max((x/k+1)*k,(y/k)*k);i<=y;i++) cnt+=(num[i]>0);
return cnt;
}
int main()
{
IOS;
cin>>t;
while(t--){
memset(num,0,sizeof num),memset(sum,0,sizeof sum);
cin>>n>>m;
rep(i,1,n) cin>>a[i];
rep(i,1,m) cin>>b[i].l>>b[i].L>>b[i].r>>b[i].R,b[i].id=i;
sort(b+1,b+m+1);
int pl=b[1].l,pr=b[1].r;
rep(i,pl,pr) add(a[i]);
ans[b[1].id]=calc(b[1].L,b[1].R);
rep(i,2,m){
while(b[i].l>pl) dec(a[pl]),pl++;
while(b[i].l<pl) pl--,add(a[pl]);
while(b[i].r>pr) pr++,add(a[pr]);
while(b[i].r<pr) dec(a[pr]),pr--;
ans[b[i].id]=calc(b[i].L,b[i].R);
}
rep(i,1,m) cout<<ans[i]<<"\n";
}
}