总结
carry不在的第一场杭电
开局我读到去年网络赛原题,臭不要脸的签上到了。赛后发现结论也是显然。
郎老师读1001极速签到,又是飞快签到的一天。
1009签到题但没什么人做。。因为一上来1005是个最小生成树,导致我一度看什么都是最小生成树,写了个二分最小生成树TLE了,发下二分+搜索就可以做了,居然,还能wa!。不过也是很少写这种二分,赛后STD也不是二分…还是水平薄弱,应该检讨。
1008郎老师提供的思路,我跟着完善了下,郎老师交了一发a了。赛后知道有更简单的操作,于是补习了一下悬线法,只看没实现过的单调栈也写了下。
1006属于是知识盲区了,赛后lls和我都补了,新技能get。
1001 题意
给n,求 (n mod 1) or (n mod 2) or … or (n mod (n - 1)) or (n mod n).
1001 思路
因为是or,我们只要考虑每一位的1即可。容易发现n-2,n-4,n-8…n-2^ k是几组特别的模数,当n-2^ k>2^ k时,可以保证答案是2^ (k+1)-1。打一下2的幂的表,二分即可。
1001 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=400505;
const int inf=0x3f3f3f3f;
int n,m,k;
vector<int>v;
void solve(){
cin>>n;
if(n<3){
cout<<0<<endl;
return ;
}
auto p=lower_bound(v.begin(),v.end(),n);
while(*p>=n)p--;
cout<<*p-1<<endl;
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
cin>>tn;
int now=1;
v.push_back(0);
v.push_back(1);
for(int i=1;i<=60;i++){
v.push_back(2*now);
now*=2;
}
while(tn--){
solve();
}
}
1005 题意
2-n的点,相互之间有lcm(a,b)的边,最小生成树。
1005 思路
数学归纳法思想,第一个点是2,这时代价是0。假设我们已经完成了前n个,考虑第n+1个点。前面n个点显然是联通的,那么我们随便连一个代价最小的边就行了。当n+1是质数时,我们连2,否则,连他一个约数即可。
答案就是3到n的2倍质数加全部合数。可以线性筛一下质数,求个前缀和,二分加一个等差求和即可。
1005 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=10000005;
const int inf=0x3f3f3f3f;
int n,m,k,q;
vector<int>prime;
int pri[maxn],sum[maxn];
int cnt;
int v[maxn];
void Euler_prime(int n){
memset(v,0,sizeof v);
for(int i=2;i<=n;i++){
if(!v[i]){
v[i]=i;
pri[++cnt]=i;
}
for(int w=1;w<=cnt;w++){
int j=pri[w];
if(j>v[i]||j>n/i) break;
v[i*j]=j;
}
}
for(int i=1;i<=cnt;i++)
sum[i]=sum[i-1]+pri[i];
}
void solve(){
cin>>n;
if(n<=2){
cout<<0<<endl;
return ;
}
int pos=lower_bound(pri+1,pri+1+cnt,n)-pri;
if(pri[pos]>n) pos--;
cout<<sum[pos]+(n*n+n-10)/2<<endl;
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
cin>>tn;
Euler_prime(10000003);
while(tn--){
solve();
}
}
1006 题意
给你一个数组,求最短且最靠左的连续区间,使其异或和大于等于k
1006 思路
首先前置芝士,异或和可以前缀和,这样连续区间可以转化为在前缀异或和数组上找两个最近的数,异或大于等于k。
直接枚举n²,这里用到了01trie树,就是把一个数字当成二进制串,放在trie上维护,它可以nlogn的求出两个数异或最大值。
我们类似的维护trie树,同时开一个index数组保留这个trie树上结点对应的最靠右结点下标。每次先搜索当前数字能否在树上异或出大于k的,如果可以,最靠右的下标是谁,之后更新答案,再把当前数插入。注意前缀和数组,所以左边下标需要加1处理。
1006 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=100505;
const int inf=0x3f3f3f3f;
int n,m,k;
int a[maxn];
int trie[maxn*31+10][2];
int index[maxn*31+10];
int tot=1;
void insert(int x,int in){
int p=1;
for(int k=30;~k;k--){
int ch=(x>>k)&1;
if(!trie[p][ch]){
trie[p][ch]=++tot;
trie[tot][0]=trie[tot][1]=0;
index[tot]=-1;
}
p=trie[p][ch];
index[p]=max(in,index[p]);
}
}
int find(int x){
int p=1;
int ret=0;
for(int k=30;~k;k--){
int ch=(x>>k)&1;
int t=(m>>k)&1;
if(trie[p][ch^1]){
p=trie[p][ch^1];
ret |= 1<<k;
if(ret>=m){
return index[p];
}
}
else{
p=trie[p][ch];
}
}
return ret>=m?index[p]:-1;
}
void solve(){
cin>>n>>m;
a[0]=0;tot=1;
trie[1][0]=trie[1][1]=0;
index[1]=-1;
int l,r,mi=inf;
bool ok=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(!ok&&a[i]>=m){
ok=1;
l=r=i;
}
a[i]^=a[i-1];
}
if(ok){
cout<<l<<' '<<r<<endl;
return ;
}
for(int i=1;i<=n;i++){
int t=find(a[i]);
if(~t&&i-t<mi){
mi=i-t,r=i,l=t;
}
insert(a[i],i);
}
if(mi==inf) cout<<-1<<endl;
else cout<<l+1<<' '<<r<<endl;
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
cin>>tn;
while(tn--){
solve();
}
}
1008 题意
二维矩阵,问满足每一列上到下递增的最大子矩阵。nm算法可过
1008 思路
预处理k矩阵,代表每个元素向下拓展保持递增的最长长度。可以用栈在nm的复杂度处理出来。
之后就是遍历每行,相当于对ki数组进行一个最大直方图面积的处理。悬线法或者单调栈可以O(m)处理,总得复杂度O(nm)。下附两个版本
1008 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=2005;
const int inf=0x3f3f3f3f;
int n,m;
int vis[maxn];
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read(){
register int x(0);register char c(gc);
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x;
}
int a[maxn][maxn],k[maxn][maxn];
int l[maxn],r[maxn];
void solve(){
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
for(int j=1;j<=m;j++){
stack<int>s;
for(int i=1;i<=n;i++){
if(s.empty()||a[i][j]>=a[s.top()][j]) s.push(i);
else{
while(s.size()){
k[s.top()][j]=i-1;
s.pop();
}
s.push(i);
}
}
while(s.size()){
k[s.top()][j]=n;
s.pop();
}
for(int i=1;i<=n;i++)
k[i][j]-=i-1;
}
//for(int i=1;i<=n;i++){
// for(int j=1;j<=m;j++)
// cout<<k[i][j]<<' ';
// cout<<endl;
//}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
l[j]=r[j]=j;
for(int j=1;j<=m;j++)
while(l[j]>1&&k[i][l[j]-1]>=k[i][j]) l[j]=l[l[j]-1];
for(int j=m;j;j--)
while(r[j]<m&&k[i][r[j]+1]>=k[i][j]) r[j]=r[r[j]+1];
for(int j=1;j<=m;j++){
ans=max(ans,k[i][j]*(r[j]-l[j]+1));
}
}
cout<<ans<<endl;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
tn=read();
while(tn--){
solve();
}
}
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=2005;
const int inf=0x3f3f3f3f;
int n,m;
int vis[maxn];
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read(){
register int x(0);register char c(gc);
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x;
}
int a[maxn][maxn],k[maxn][maxn];
void solve(){
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
for(int j=1;j<=m;j++){
stack<int>s;
for(int i=1;i<=n;i++){
if(s.empty()||a[i][j]>=a[s.top()][j]) s.push(i);
else{
while(s.size()){
k[s.top()][j]=i-1;
s.pop();
}
s.push(i);
}
}
while(s.size()){
k[s.top()][j]=n;
s.pop();
}
for(int i=1;i<=n;i++)
k[i][j]-=i-1;
}
int ans=0;
for(int i=1;i<=n;i++){
k[i][m+1]=0;
stack<pair<int,int> >s;
for(int j=1;j<=m+1;j++){
int w=0;
while(s.size()&&k[i][j]<=s.top().first){
w+=s.top().second;
ans=max(ans,s.top().first*w);
s.pop();
}
s.push({k[i][j],1+w});
}
}
cout<<ans<<endl;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
tn=read();
while(tn--){
solve();
}
}
1009 题意
n点分成k组,要求同组必须至少一条路上最长边比d小,不同组不能有任何一条路最长边比d小,问最小化d。
1009 思路
官方题解是边排序,维护当前d值,每次将新的权值一样的边全部链接上,如果联通块等于k个,就是有解。
比赛时整的二分,二分d看能不能连成k个块。
1009 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=500505;
const int inf=0x3f3f3f3f;
int n,m,k;
int vis[maxn];
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read(){
register int x(0);register char c(gc);
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x;
}
int ans=inf;
vector<pair<int,int>>e[maxn];
void init(){
for(int i=1;i<=n;i++)
e[i].clear();
ans=inf;
}
int now;
void dfs(int x){
for(auto p:e[x]){
int y=p.first,w=p.second;
if(w<=now&&vis[y]!=now){
vis[y]=now;
dfs(y);
}
}
}
int check(int x){
int tot=0;
now=x;
for(int i=0;i<=n;i++) vis[i]=-1;
for(int i=1;i<=n;i++){
if(vis[i]!=x){
vis[i]=x;
tot++;
dfs(i);
}
}
if(k==tot) ans=min(ans,x);
return tot;
}
void solve(){
n=read(),m=read(),k=read();
init();
int ma=0;
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
e[u].push_back({v,w});
e[v].push_back({u,w});
ma=max(w,ma);
}
if(k==n){
printf("0\n");
return;
}
int l=0,r=ma+10,mid;
while(l<r){
mid=(l+r)/2;
//cout<<mid<<endl;
int t=check(mid);
if(t<=k){
r=mid;
}
else{
l=mid+1;
}
}
if(ans==inf) ans=-1;
printf("%d\n",ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
tn=read();
while(tn--){
solve();
}
}
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
//#define int long long
//#define double long double
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int maxn=500505;
const int inf=0x3f3f3f3f;
int n,m,k;
int fa[maxn];
int find(int x){
return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
void init(){
for(int i=1;i<=n;i++) fa[i]=i;
}
struct Edge{
int u,v,w;
}edge[maxn];
bool cmp(Edge a,Edge b){
return a.w<b.w;
}
void solve(){
cin>>n>>m>>k;
init();
for(int i=1;i<=m;i++){
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
if(k==n){
cout<<0<<endl;
return;
}
sort(edge+1,edge+1+m,cmp);
edge[m+1]={0,0,inf};
int last=-1;
int cnt=n;
for(int i=1;i<=m+1;i++){
if(edge[i].w==last){
int fx=find(edge[i].u),fy=find(edge[i].v);
if(fx!=fy){
cnt--;
fa[fx]=fy;
}
}
else{
if(cnt<k){
cout<<-1<<endl;
return ;
}
else if(cnt==k){
cout<<last<<endl;
return ;
}
last=edge[i].w;
i--;
}
}
cout<<-1<<endl;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
IOS
int tn=1;
cin>>tn;
while(tn--){
solve();
}
}
1010 题意
给出数列,每次询问l到r上low到high上有多少个位置有数字。
1010 思路
想着想着就忘了一个x只对应一个y了。
横坐标莫队,纵坐标分块。
莫队的指针移动是O(1)的,满足莫队使用条件。
莫队的移动是msqrt n,对应分块修改o(1)
莫队的查询是m,对应分块查询是O(sqrt m)
所以总的复杂度也是m根号m这个级别的,1e5不卡能过
1010 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=100505;
const int maxm=1005;
const int inf=0x3f3f3f3f;
int n,m,k;
int len;
int a[maxn],id[maxn];
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read(){
register int x(0);register char c(gc);
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x;
}
int ans[maxn];
int cnt[maxn];
int num[maxn];
int low,high;
void add(int x){
//cout<<cnt[a[x]]<<endl;
if(!cnt[a[x]])
num[id[a[x]]]++;//,cout<<1<<endl;
cnt[a[x]]++;
}
void del(int x){
//cout<<cnt[a[x]]<<endl;
cnt[a[x]]--;
if(!cnt[a[x]])
num[id[a[x]]]--;//,cout<<1<<endl;
}
struct Q{
int l,r,low,high;
int id;
}q[maxn];
bool cmp(Q A,Q B){
return (id[A.l] ^ id[B.l]) ? id[A.l] < id[B.l] : ((id[A.l] & 1) ? A.r < B.r : A.r > B.r);
}
int query(int x,int y){
int ret=0;
if(id[x]==id[y]){
for(int i=x;i<=y;i++)
if(cnt[i]) ret++;
return ret;
}
for(int i=x;id[i]==id[x];i++){
if(cnt[i]) ret++;
}
for(int i=id[x]+1;i<id[y];i++)
ret+=num[i];
for(int i=y;id[i]==id[y];i--){
if(cnt[i]) ret++;
}
return ret;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
tn=read();
len=sqrt(maxn);
for(int i=0;i<maxn;i++)
id[i]=(i-1)/len+1;
while(tn--){
n=read(),m=read();
memset(cnt,0,sizeof cnt);
memset(num,0,sizeof num);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++){
int x1=read(),y1=read(),x2=read(),y2=read();
q[i]={x1,x2,y1,y2,i};
}
sort(q+1,q+1+m,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++){
int ll=q[i].l,rr=q[i].r;
low=q[i].low,high=q[i].high;
while(r<rr) add(++r);
while(l>ll) add(--l);
while(r>rr) del(r--);
while(l<ll) del(l++);
ans[q[i].id]=query(low,high);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
}