總結
第三次訓練感覺我的狀態比上一次又好了一些,但還不夠熟練,主要是小錯誤不斷犯,不能一次性打出bug較少的代碼。
A - Ascending Rating (單調隊列+路徑壓縮)
description
給定長度爲n(<=1e7)的序列,每次從點l開始,每次在x點上,選擇在[l,l+m-1]比當前點x值更大的點並跳過去,設maxrating爲最後站立的點的值,count爲經歷的點的個數,統計從每個點開始的maxrating和count
輸出A,B,有多組數據
solution
我們先用單調隊列找出每個點i往後第一個比他大的數的位置fa[i],然後從前往後枚舉x,每次像並查集一樣往前搜索並壓縮路徑,但只回溯的範圍不超過x+m-1,壓縮路徑並更新路徑上的點的答案即可。
最狗的是這裏的n在1e7的範圍,直接像並查集遞歸找可能會爆棧(???,我tm第一次會並查集爆棧!!)
code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const ll maxn=1e7+5;
ll a[maxn],d[maxn],f[maxn],fa[maxn];
ll n,m,i,t,j,k,l,x,y,z,mo,p,T,r,mid,q;
ll ans,ans1;
ll read(){
char ch=getchar();ll x=0;
while (ch<48 || ch>57) ch=getchar();
while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
return x;
}
int getfa(int x,int y){
d[0]=1;d[1]=x;
for(;fa[x]-y<=m-1;x=fa[x])d[++d[0]]=fa[x];
rp(i,d[0]-1,1){
if (d[i+1]!=x)f[d[i]]+=f[d[i+1]];
fa[d[i]]=x;
}
return x;
}
int main(){
scanf("%lld",&T);
while (T--){
scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&m,&k,&p,&q,&r,&mo);
fo(i,1,k)a[i]=read();
fo(i,k+1,n)a[i]=(p*a[i-1]+q*i+r)%mo;
d[0]=0;
rp(i,n,1){
while (d[0] && a[d[d[0]]]<=a[i]) d[0]--;
if (d[0]) fa[i]=d[d[0]];
else fa[i]=n+1;
d[++d[0]]=i;f[i]=1;
}
ans=ans1=0;
fo(i,1,n-m+1){
x=getfa(i,i);
ans+=(a[x]^i);
if (x==i) t=f[i];
else t=f[i]+f[x];
ans1+=(t^i);
}
printf("%lld %lld\n",ans,ans1);
}
}
C Dynamic Graph Matching (狀壓dp)
description
給定n(<=10)個點,初始互不連通,有m(<3e4)個操作,每次操作可以加入或刪除已存在的一條邊,要求每次操作後輸出選擇k(0…n/2)條端點沒有交集的邊的方案。
solution
設f[s]表示狀態集爲S,若爲加入,則枚舉含u,v的狀態S,f[s]+=f[s-(1<<u)-(1<<v)],若爲減則同理。時間複雜度O(m*2^n)
code
#include<bits/stdc++.h>
using namespace std;
const int mo=1e9+7;
int main(){
char s[10];
long long t,n,m,dp[1<<11],ans[20],x,y,counts[1<<11];
cin>>t;
for(int i=1;i<1<<11;i++) counts[i]=__builtin_popcount(i);
while(t--){
cin>>n>>m;
memset(dp,0,sizeof(dp));
dp[0]=1;
while(m--){
memset(ans,0,sizeof(ans));
scanf("%s %d %d",s,&x,&y);
x--;
y--;
if(s[0]=='+'){
for(int i=0;i<=(1<<n)-1;i++)
if((i&(1<<x))&&(i&(1<<y)))
dp[i]=(dp[i]+dp[i-(1<<x)-(1<<y)]+mo)%mo;
}
else{
for(int i=0;i<=(1<<n)-1;i++)
if((i&(1<<x))&&(i&(1<<y)))
dp[i]=(dp[i]-dp[i-(1<<x)-(1<<y)]+mo)%mo;
}
for(int i=0;i<=(1<<n)-1;i++) ans[counts[i]]=(ans[counts[i]]+dp[i]+mo)%mo;
for(int i=1;i<=n/2;i++){
cout<<ans[i*2];
if(i!=n/2) cout<<" ";
}
cout<<endl;
}
}
return 0;
}
D Dynamic Graph Matching (結論題?)
description
求第k個歐拉函數爲合數的數
solution
同隊的大佬打表出結論……
我們知道歐拉函數是積性函數,若a,b互質,φ(ab)=φ(a)φ*(b),在線性篩中,b永遠是質數,所以若a,b不互質,那麼φ(ab)=φ(a)*b,這說明了什麼?除了開始的幾個φ(a)有可能是質數外,其他必定是合數!!!
然後,沒然後了。
code
#include<bits/stdc++.h>
using namespace std;
int main(){
long long t,k;
cin>>t;
while(t--){
cin>>k;
k=k+5;
if(k==6) k--;
cout<<k<<endl;
}
return 0;
}
F Grab The Tree (博弈)
description
一棵樹,每個節點上有權值,現在在樹上任意取一些點,要求這些點在樹上不能相鄰,要求這些點最優條件下的xor和是否比剩下的點的xor和大。
solution
這題好險不是我打,否則我就往樹形dp那邊去想了……
我們發現取下來的點的異或和xor上剩下的點的異或和爲所有點的異或和,若所有點的異或和不爲0,那麼我們只要取最高位爲1的隨便一個點即可,若爲0肯定平局。
code
#include<iostream>
#include<cstdio>
using namespace std;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
return f*s;
}
int main(){
int T=read();
while(T--){
int sum=0;
int n=read();
for(int i=0;i<n;i++)
sum^=read();
for(int i=1;i<n;i++)read(),read();
if(sum==0)printf("D\n");
else printf("Q\n");
}
return 0;
}
G - Interstellar Travel (計算幾何)
description
有n個點(x,y),從i到j的(xi<xj)的代價爲xi* yj-xj* yi,(保證x1<x2,x3,……<xn,y1=yn=0,0<=xi,yi<=1e9),要求一條從1到n的路徑,使得代價最小
solution
我們假設現在有(x1,y1),(x2,y2),(x3,y3)(x1<x2<x3)3個點,我們計算什麼時候從1直接跳到3比1到2再到3更優
從1到3:x1y3-x3y1
從1到2到3:x1y2-x2y1+x2y3-x3y2
下減上得:x1y2-x2y1+x2y3-x3y2-x1y3+x3y1>0
化簡得:x1(y2-y3)+x3(y1-y2)-x2(y1-y2+y2-y3)>0
(y2-y3)/(x2-x3)>(y1-y2)/(x1-x2)
反之,若k32<k21,那麼2就成了必須點,仔細想想,這不就是上凸殼嗎?所以按x軸排序後求出了一個上凸殼即可
code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const ll maxn=2e5+5;
ll n,m,i,t,j,k,l,x,y,z,T;
ll d[maxn],p[maxn];
struct code{
ll x,y,id;
}a[maxn];
bool cmp(code x,code y){
return x.x<y.x || x.x==y.x && x.y>y.y || x.x==y.x && x.y==y.y && x.id<y.id;
}
bool pan(ll x,ll y,ll z){
if (a[y].x==a[z].x) return 0;
ll p=(a[y].y-a[x].y)*(a[z].x-a[y].x),
q=(a[z].y-a[y].y)*(a[y].x-a[x].x);
if (p<q) return 1;
else if (p>q) return 0;
return a[y].id>a[z].id;
}
int main(){
scanf("%lld",&T);
while (T--){
scanf("%lld",&n);
fo(i,1,n)scanf("%lld%lld",&a[i].x,&a[i].y),a[i].id=i;
sort(a+1,a+n+1,cmp);t=2;
fo(j,3,n)
if (pan(1,t,j)) t=j;
d[1]=1;d[0]=2;d[2]=t;
fo(j,t+1,n){
while (pan(d[d[0]-1],d[d[0]],j) && d[0]>2) d[0]--;
if (a[j].x!=a[d[d[0]]].x)d[++d[0]]=j;
}
fo(i,1,d[0]-1) printf("%lld ",a[d[i]].id);
printf("%lld\n",a[d[d[0]]].id);
}
}
L - Visual Cube (模擬)
description
給你長方體得長寬高,打印出長方體得立體圖形
solution
別問我怎麼打出來的,我也不知道他怎麼打出來的……
code
#include<iostream>
#include<cstdio>
using namespace std;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
return f*s;
}
char t[110][110];
void dodo(int x,int y,int a,int c){
int xx=x,yy=y;
t[x][y]='+';t[x+1][y-1]='/';
while(a--){
y--;
t[xx][y]='-';
y--;
t[xx][y]='+';
t[xx+1][y-1]='/';
}
while(c--){
x++;
t[x][yy]='|';
x++;
t[x][yy]='+';
t[x+1][yy-1]='/';
}
}
void work(int a,int b,int c){
int m=b+b+c+c+1;
int n=a+a+b+b+1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
t[i][j]='.';
for(int i=b+b+1;i<=m;i++,i++){
for(int j=1;j<=a+1;j++)
t[i][j+j-1]='+',t[i][j+j]='-';
t[i][a+1+a+1]='.';
for(int j=1;j<=a+1;j++)
t[i+1][j+j-1]='|';
}
for(int i=1;i<=b;i++)
dodo(i+i-1,n+2-i-i,a,c);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)
putchar(t[i][j]);
putchar('\n');
}
}
int main(){
int T=read();
while(T--){
int a=read(),b=read(),c=read();
work(a,b,c);
}
return 0;
}
M - Walking Plan (分塊+floyd)
description
一張n(<=50)個點m(<=10000)條邊(無自環)得有向圖,q(<=1e5)個詢問,每次詢問從s到t經過至少k條邊的最小花費
solution
事實證明,分塊比二分更爲優秀awsl
我們按100分塊,設f[k][i][j]表示從i到j恰好走100*k條邊的最小費用
d[k][i][j]表示從i到j恰好走k條邊的最少費用
但是題目說的是至少走k條邊,因此我們需要對d數組進行一些改造
手動的讓d多跑一些邊(比如跑120條邊)
然後從120到1倒着取min
(爲什麼是120這個數字?隨緣取的,原則上更大一些更安全?
正確性?(或許可以證明吧、然而不太會
答案就是min(f[k/100][s][j]+f[x%100][j][t])
(通過j點進行中轉)
複雜度O(T(qn+100n3)),複雜度O(T(qn+100n3))
code
#include<bits/stdc++.h>
typedef long long LL;
const int oo=0x3f3f3f3f;
using namespace std;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
return f*s;
}
const int N=60;
int f[150][N][N],d[160][N][N],n,m;
void work(){
n=read(),m=read();
memset(f,0x3f,sizeof(f));
memset(d,0x3f,sizeof(d));
for(int i=1;i<=n;i++)
f[0][i][i]=d[0][i][i]=0;
for(int i=0;i<m;i++){
int x=read(),y=read();
d[1][x][y]=min(d[1][x][y],read());
}
for(int t=2;t<=120;t++)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[t][i][j]=min(d[t][i][j],d[t-1][i][k]+d[1][k][j]);
for(int t=119;t>=0;t--)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[t][i][j]=min(d[t][i][j],d[t+1][i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[1][i][j]=d[100][i][j];
for(int t=2;t<=100;t++)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[t][i][j]=min(f[t][i][j],f[t-1][i][k]+f[1][k][j]);
int q=read();
for(int i=0;i<q;i++){
int s=read(),t=read(),k=read();
int ans=oo;
for(int j=1;j<=n;j++)
ans=min(ans,f[k/100][s][j]+d[k%100][j][t]);
if(ans==oo)ans=-1;
if(ans==oo)ans=-1;
printf("%d\n",ans);
}
}
int main(){
int T=read();
while(T--)work();
return 0;
}