和初三學長們一起打的比賽,被人家爆殺了。
T1 最短路 20Pts
原題 Cow Toll Paths G。
正解是按點權排序後跑一遍 Floyd,歪解是多迭代幾遍。
賽時沒開 long long
\(80 \to 20\)。
點選檢視程式碼
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=305;
pii a[N][N];
int n,m,v[N],x,y,q,s,t,w;
main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)a[i][j]={0,0};
else a[i][j]={0x3f3f3f3f3f3f3f3f,0};
}
}
for(int i=1;i<=n;i++)cin>>v[i];
for(int i=1;i<=m;i++){
cin>>x>>y>>w;
w=min(w,a[x][y].first);
a[x][y].first=a[y][x].first=w;
a[x][y].second=a[y][x].second=max(v[x],v[y]);
}
int T=3;
while(T--)
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(i==k)continue;
for(int j=1;j<=n;j++){
if(j==k||i==j)continue;
if(a[i][j].first+a[i][j].second>a[i][k].first+a[k][j].first+max(a[i][k].second,a[k][j].second)){
a[i][j].first=a[i][k].first+a[k][j].first;
a[i][j].second=max(a[i][k].second,a[k][j].second);
}
}
}
}
while(q--){
cin>>x>>y;
if(a[x][y].first==0x3f3f3f3f3f3f3f3f)cout<<"-1\n";
else cout<<a[x][y].first+a[x][y].second<<"\n";
}
return 0;
}
T2 方格取數 40Pts
原題 KUP-Plot purchase。
首先判掉在 \([k,2k]\)(直接輸出) 和 \((2k,+\infty)\)(一定不選)的數,剩下的數就都在 \((1,k-1)\) 之間了;
此時如果我們能找出一個總和大於 \(2k\) 的矩陣,那麼一定有解,因為其一定包括了一個總和在 \([k,2k]\) 之間的矩陣。
所以我們先找出一個總和大於 \(2k\) 的極大矩陣,然後每次刪去一行,如果這一行的和大於 \(k\) 就在這一行中一個一個刪,否則繼續在大矩陣中進行。
單調棧找最大矩陣即可。
賽時想到了單調棧,但是沒打出來,隨機化 40。
點選檢視程式碼
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=2005;
int n,k,a[N][N],sum[N][N],l[N][N],r[N][N],s[N][N],mp[N][N];
int get(int x,int y,int xx,int yy){
return sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1];
}
main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
if(a[i][j]>=k&&a[i][j]<=k*2){
cout<<i<<" "<<j<<" "<<i<<" "<<j;
return 0;
}
else mp[i][j]=(a[i][j]<k);
sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[i][j])s[i][j]=s[i-1][j]+1;
else s[i][j]=0;
}
}
for(int i=1;i<=n;i++){
stack<int> ss;
for(int j=1;j<=n+1;j++){
while(!ss.empty()&&s[i][j]<s[i][ss.top()]){
r[i][ss.top()]=j-1;
ss.pop();
}
ss.push(j);
}
while(!ss.empty())ss.pop();
for(int j=n;j>=0;j--){
while(!ss.empty()&&s[i][j]<s[i][ss.top()]){
l[i][ss.top()]=j+1;
ss.pop();
}
ss.push(j);
}
for(int j=1;j<=n;j++){
int now=get(i-s[i][j]+1,l[i][j],i,r[i][j]);
if(now>=k){
if(now<=k*2){
cout<<i-s[i][j]+1<<" "<<l[i][j]<<" "<<i<<" "<<r[i][j];
return 0;
}
else{
for(int h=l[i][j]+1;h<=r[i][j];h++){
int noww=sum[i][h]-sum[i][l[i][j]-1];
if(noww>=k&&noww<=2*k){
cout<<i<<" "<<l[i][j]<<" "<<i<<" "<<h;
return 0;
}
}
}
}
}
}
cout<<-1;
return 0;
}
T3 陣列 0Pts
原題 Please, another Queries on Array?。
對於尤拉函式,有式子 \(\varphi(x)=x \ \cdot \ \prod_{i=1}^n\limits (\frac{p_i-1}{p_i})\),其中 \(p_i\) 為 \(x\) 的所有質因數。
區間積可以用線段樹維護,難點在於質因子的維護。
透過一些技術手段,我們發現,在 300 以內只有 62 個質因子,剛好可以用一個 long long
來狀壓。
同樣丟到線段樹裡即可。
賽時因為 1<<62
爆零了。
1<<62
\(\to\) 1ll<<62
\(0 \to 100\)
點選檢視程式碼
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int N=4e5+5;
int n,a[N],l,r,x,q;
string op;
int prim[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293};
inline int qp(int a,int b,int mod=mod){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
inline int getp(int x){
int p=0;
for(int i=0;i<62;i++){
if(x%prim[i]==0)p|=(1ll<<i);
}
return p;
}
struct tree{
int l,r,mul,lzm;
int p,lzp;
int len(){return r-l+1;}
}t[N<<2];
void pushup(int k){
t[k].mul=t[k<<1].mul*t[k<<1|1].mul%mod;
t[k].p=t[k<<1].p|t[k<<1|1].p;
}
void build(int k,int l,int r){
t[k]={l,r,a[l],1};
if(l==r){
int p=getp(a[l]);
t[k].p=p;t[k].lzp=0;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void pushdown(int k){
if(t[k].lzm!=1){
t[k<<1].mul=t[k<<1].mul*qp(t[k].lzm,t[k<<1].len())%mod;
t[k<<1].lzm=t[k<<1].lzm*t[k].lzm%mod;
t[k<<1|1].mul=t[k<<1|1].mul*qp(t[k].lzm,t[k<<1|1].len())%mod;
t[k<<1|1].lzm=t[k<<1|1].lzm*t[k].lzm%mod;
t[k].lzm=1;
}
if(t[k].lzp!=0){
t[k<<1].p|=t[k].lzp;
t[k<<1].lzp|=t[k].lzp;
t[k<<1|1].p|=t[k].lzp;
t[k<<1|1].lzp|=t[k].lzp;
t[k].lzp=0;
}
}
void update(int k,int l,int r,int val,int p){
if(l<=t[k].l&&t[k].r<=r){
t[k].mul=t[k].mul*qp(val,t[k].len())%mod;
t[k].lzm=t[k].lzm*val%mod;
t[k].p|=p;
t[k].lzp|=p;
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid)update(k<<1,l,r,val,p);
if(r>mid)update(k<<1|1,l,r,val,p);
pushup(k);
}
pii query(int k,int l,int r){
if(l<=t[k].l&&t[k].r<=r){
return {t[k].mul,t[k].p};
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
pii ans={1,0};
if(l<=mid){
pii L=query(k<<1,l,r);
ans.first=ans.first*L.first%mod;
ans.second|=L.second;
}
if(r>mid){
pii R=query(k<<1|1,l,r);
ans.first=ans.first*R.first%mod;
ans.second|=R.second;
}
return ans;
}
int inv[500];
main(){
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
for(int i=1;i<=400;i++)inv[i]=qp(i,mod-2);
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(q--){
cin>>op;
if(op=="1"){
cin>>l>>r>>x;
int p=getp(x);
update(1,l,r,x,p);
}
else{
cin>>l>>r;
pii tmp=query(1,l,r);
int ans=tmp.first,p=tmp.second;
for(int i=0;i<62;i++){
if((p>>i)&1){
ans=ans*(prim[i]-1)%mod*inv[prim[i]]%mod;
}
}
cout<<ans<<"\n";
}
}
return 0;
}
T4 樹 30Pts
原題 ODW。
根號分治。
設塊長為 \(T\)。
預處理出步長為 \([1,\sqrt T]\) 時每個點向上跳到根的點權和以及每個點的 \(1 \backsim \sqrt T\)
大於 \(T\) 則暴力跳,否則樹上差分。
這題資料很水,暴力有 70,能過除了鏈以外的所有點;加個對鏈的暴力就過了。
但賽時樹剖 LCA 打鍋了導致 \(100 \to 30\)
後記
還是學到了很多的,也確實是技不如人。
之後要注意程式碼細節問題,儘量不要在不該掛的地方掛分。