日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

AGC001 补题小结

發布時間:2023/12/31 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AGC001 补题小结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Problem A BBQ Easy

簡要題意:給定 \(2n\) 個數字,兩兩配對,一對的貢獻為它們的 \(min\) ,求最大貢獻和。\(n\le 100\)

tag:小學生貪心

題解:排個序,求奇數位之和,證明顯然。

#include <cstdio> using namespace std; const int N=105; int a[N]; int main (){int n,ans=0;scanf ("%d",&n);for (int i=1;i<=(n<<1);i++) scanf ("%d",&a[i]);sort(a+1,a+(n<<1)+1);for (int i=1;i<=n;i++) ans+=a[(i-1)<<1|1];printf ("%d",ans);return 0; }

Problem B Mysterious Light

簡要題意:給一個邊長為 \(n\) 的等邊三角形,從一個線段 \(ab\) 上的點 \(x\) 發射激光,邊緣是鏡子,遇到會被反射,遇到自己的軌跡也會被反射,回到原點被吸收,(下圖是一個例子)問路徑總長. \(n\le 10^{12}\)

tag:模擬,遞歸

題解:每次要做的事情就是將一個平行四邊形切成邊長為短邊長度的若干等邊三角形,直到切不出來位置,切不出來的時候,就出現了一個新的平行四邊形,遞歸即可。

#include <cstdio> #define ll long long ll n,x; inline ll work(ll l1,ll l2){if (!l2) return -l1;return work(l2,l1%l2)+2*(l1/l2)*l2; } int main() {scanf("%lld%lld",&n,&x);printf("%lld\n",work(x,n-x)+n);return 0; }

Problem C Shorten Diameter

簡要題意:給你一棵 \(n\) 個節點的樹,你可以刪除葉子,問最少刪除多少點之后可以使它的直徑小于等于 \(k\) \(n\le 1000\)

tag:圖論,貪心

題解:根據 \(k\) 的奇偶性分類做,如果是偶數,枚舉一個點作為直徑中點,把距離這個點大于 \(\frac{k}{2}\)的點全部砍掉,如果是奇數,枚舉一條邊, 把距離這條邊大于 \(\frac{k-1}{2}\) 的點砍掉,然后取 \(min\) 即可

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=2005; int Head[N],Next[N<<1],Adj[N<<1],tot=0,dis[N]; int u[N],v[N]; inline void addedge(int u,int v){Next[++tot]=Head[u];Head[u]=tot;Adj[tot]=v;Next[++tot]=Head[v];Head[v]=tot;Adj[tot]=u; } inline void dfs(int x,int f){for (int e=Head[x];e;e=Next[e])if (Adj[e]!=f) dis[Adj[e]]=dis[x]+1,dfs(Adj[e],x); } int main (){int n,k;scanf ("%d%d",&n,&k);for (int i=1;i<n;i++){scanf ("%d%d",&u[i],&v[i]);addedge(u[i],v[i]);}int ans=n;if (k&1){for (int i=1;i<n;i++){dis[u[i]]=dis[v[i]]=0;dfs(u[i],v[i]),dfs(v[i],u[i]);int cnt=0;for (int j=1;j<=n;j++) if (dis[j]>(k/2)) cnt++;ans=min(ans,cnt);}}else{for (int i=1;i<=n;i++){dis[i]=0;int cnt=0;dfs(i,0);for (int j=1;j<=n;j++) if (dis[j]>(k/2)) cnt++;ans=min(ans,cnt);}}printf ("%d",ans);return 0; }

Problem D Arrays and Palindrome

簡要題意:給定你 \(a\) 序列,請你重排它并構造一個 \(b\) 序列,使得兩個序列元素和均為 \(n\)
對于一個長度為 \(n\) 的字符串,滿足如果前 \(a_1\) 個是回文串,接下來 \(a_2\) 個是回文串……且前 \(b_1\) 個是回文串,接下來 \(b_2\) 個是回文串……那么這個字符串處處相同。

tag:圖論,思維,貪心,構造

題解:建立一個圖論模型。對于欽定相等的兩個點,給它們連一條邊,然后一個聯通塊內的所有點都要相等。那對于一個長度為 \(n\) 的回文串,其實就是欽定了 \(i\)\(n-i+1\) 要相等,相當于在圖中連了 \(\frac{n}{2}\) 條邊。為了讓 \(n\) 個點連通,我們需要至少 \(n-1\) 條邊。對于一個由若干段回文構成的序列,它至多連出 \(\frac{n-odd}{2}\) 條邊,其中 \(odd\) 表示大小為奇數的段數。可以看出,若一開始給定的數列中,奇數大于 \(2\) 個,必然無解。那么對于一個有解的數列,把奇數放在首尾得到序列 \(1\) ,第一個數減一、最后一個數加一得到序列 \(2\) 。這樣,每一段都是錯開的,每一條邊都必然連接兩個原本不連通的塊。因為題目要求不能輸出 \(0\) ,所以需要再特判一下。

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=1005; int a[N]; int main (){int n,m;scanf ("%d%d",&n,&m);int t1=m,t2=1;for (int i=1;i<=m;i++) scanf ("%d",&a[i]);int cnt=0;for (int i=1;i<=m;i++)if (a[i]&1) {cnt++;if (cnt==1) t1=i;if (cnt==2) t2=i;if (cnt>2){puts("Impossible");return 0;}}swap(a[m],a[t1]);swap(a[1],a[t2]);if (m==1&&a[1]==1) {puts("1\n1\n1");return 0;}for (int i=1;i<=m;i++) printf("%d ",a[i]);if (m==1) {printf("\n2\n%d 1\n",a[1]-1);return 0;}printf ("\n%d\n%d ",a[m]>1?m:m-1,a[1]+1);for (int i=2;i<m;i++) printf("%d ",a[i]);if (a[m]>1) printf("%d\n",a[m]-1);return 0; }

Problem E BBQ Hard

簡要題意:有 \(n\) 個數對 \((A_i,B_i)\) ,求出 \(\sum_{i=1}^{n}\sum_{j=i+1}^n{A_i+B_i+A_j+B_j \choose A_i+A_j}\) 答案對 \(10^9+7\) 取模 。\(n\le 2\times 10^5,\ A_i,B_i\le 1000\)

tag:組合意義,計數,dp

題解:神仙題,毫無思路 可能因為我太菜了。

考慮這個東西的組合意義,我們知道 \(\binom{x+y}{x}\) 可以表示成坐標軸上 \((0,0)\) 走到 \((x,y)\) ,每次只能向右或上走的方案數,所以問題變成了從 \((0,0)\) 走到 \((A_i+A_j,B_i+B_j)\) 的方案數。

我們平移一下這兩個點,變成了從 \((-A_i,-B_i)\) 走到 \((A_j,B_j)\) 的方案數,那么我們換一個思維,把所有的 \((-A_i,-B_i)\) 作為起點,求出所有的 \((A_j,B_j)\) 到它的路徑條數。

那么我們考慮遞推,對于每一個 \(i\) ,給 \((-A_i,-B_i)\) 的值 \(+1\),然后 \(dp[i][j]=dp[i-1][j]+dp[i][j-1]\)

那么答案就是 \(\frac{\sum_{i=1}^ndp[A_i][B_i]-\binom{2A_i+2B_i}{2A_i}}{2}\),后面那個東西是 \((-A_i,-B_i)\)\((A_i,B_i)\) 的方案數,多算了減掉

時間復雜度 \(O(n+A\times B)\)

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=2e5+5,M=2005,Mod=1e9+7; int a[N],b[N]; int dp[M<<1][M<<1],fac[M<<2],inv[M<<2]; #define C(n,m) (1ll*fac[n]*inv[m]%Mod*inv[n-m]%Mod) #define dp(i,j) dp[i+M][j+M] inline int qpow(int a,int b){int ans=1;while (b){if (b&1) ans=1ll*ans*a%Mod;b>>=1,a=1ll*a*a%Mod;}return ans; } inline int mo(int x){return x>=Mod?x-Mod:x;} int main (){int n;scanf ("%d",&n);for (int i=1;i<=n;i++)scanf ("%d%d",&a[i],&b[i]);fac[0]=1;for (int i=1;i<=8000;i++) fac[i]=1ll*fac[i-1]*i%Mod;inv[8000]=qpow(fac[8000],Mod-2);for (int i=7999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%Mod;for (int i=1;i<=n;i++) dp(-a[i],-b[i])++;for (int i=-2000;i<=2000;i++)for (int j=-2000;j<=2000;j++)dp(i,j)=mo(dp(i,j)+mo(dp(i-1,j)+dp(i,j-1)));int ans=0;for (int i=1;i<=n;i++) ans=((ans+dp(a[i],b[i])-C(2*(a[i]+b[i]),2*a[i]))%Mod+Mod)%Mod;printf ("%d",1ll*ans*qpow(2,Mod-2)%Mod);return 0; }

update on 2019.04.25

如果僅僅滿足 \(\sum_{i=1}^n(x_i+y_i)\le 2\times 10^7\) ,也是可以做的

我們觀察到第一象限的任何一個點到第三象限必定經過 \(y=-x\) 這條直線,那么我們考慮枚舉經過的是哪一個點

時間復雜度 \(O(\sum x_i+y_i)\)

Problem F Wide Swap

簡要題意:給定一個元素集合 \(\{1,2....N\}\) 的排列 \(P\) ,當有 \(i,j\) 滿足 \(j-i\ge K\)\(|P_i-P_j|=1\) 可以交換 \(P_i,P_j\)

求可能的字典序中最小的排列。\(N\le 5\times 10^5\)

tag:拓撲排序,線段樹,貪心,思維

題解:依然毫無思路

首先考慮調換權值與下標的位置,那么就可以把題目轉換成:相鄰元素且權值差 \(\ge k\) 的可以換順序,讓前面的權值盡量小的序列。

從位置 \(i\) 向后面的 \(j\) 比較,如果滿足條件 \(abs(a[i]-a[j])<k\) ,那么 \(i,j\) 的相對位置關系就確定了,可以連邊,這樣問題就變成了一個明顯的拓撲排序。

不過這樣連邊數是 \(N^2\) 級別的,需要優化。

因為要求前面的權值盡可能小,所以連向的是最小的邊,所以從 \(a[i]\)\((a[i]+1,a[i]+k-1)\) 中下標最小的連邊就可以了,反過來,也要向 \((a[i]-k+1,a[i]-1)\) 中下標最大的連邊,這樣可以包含所有情況,因為它們的相對位置一定也靠連邊確定了。

這樣,我們把邊數優化到了 \(O(n)\) 級別,算法時間復雜度 \(O(nlogn)\)

#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; priority_queue <int , vector <int >,greater <int > > Q; const int N=500005; #define mid ((l+r)>>1) int a[N],val[N<<2]; inline int query(int root,int l,int r,int L,int R){if (r<L||l>R) return 0;if (L<=l&&r<=R) return val[root];return max(query(root<<1,l,mid,L,R),query((root<<1)|1,mid+1,r,L,R)); } inline void update(int root,int l,int r,int x,int y){if (l==r) {val[root]=y;return;}if (x<=mid) update(root<<1,l,mid,x,y);else update((root<<1)|1,mid+1,r,x,y);val[root]=max(val[root<<1],val[(root<<1)|1]); } int Head[N],Next[N<<1],Adj[N<<1],tot=0,d[N],ans[N]; inline void addedge(int u,int v){Next[++tot]=Head[u],Head[u]=tot,Adj[tot]=v,d[v]++;} int main (){int n,k;scanf ("%d%d",&n,&k);for (int i=1,x;i<=n;i++) scanf ("%d",&x),a[x]=i;for (int i=1;i<=n;i++){int t=query(1,1,n,max(1,a[i]-k+1),a[i]);if (t) addedge(a[t],a[i]);t=query(1,1,n,a[i],min(n,a[i]+k-1));if (t) addedge(a[t],a[i]);update(1,1,n,a[i],i);}for (int i=1;i<=n;i++) if (!d[i]) Q.push(i);int Time=0;while (!Q.empty()){int x=Q.top();ans[x]=++Time;Q.pop();for (int e=Head[x];e;e=Next[e]){d[Adj[e]]--;if (!d[Adj[e]]) Q.push(Adj[e]);}}for (int i=1;i<=n;i++) printf ("%d\n",ans[i]);return 0; }

轉載于:https://www.cnblogs.com/crazyzh/p/10883746.html

總結

以上是生活随笔為你收集整理的AGC001 补题小结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。