AGC001 补题小结
Problem A BBQ Easy
簡要題意:給定 \(2n\) 個數(shù)字,兩兩配對,一對的貢獻(xiàn)為它們的 \(min\) ,求最大貢獻(xiàn)和。\(n\le 100\)
tag:小學(xué)生貪心
題解:排個序,求奇數(shù)位之和,證明顯然。
#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\) 上的點(diǎn) \(x\) 發(fā)射激光,邊緣是鏡子,遇到會被反射,遇到自己的軌跡也會被反射,回到原點(diǎn)被吸收,(下圖是一個例子)問路徑總長. \(n\le 10^{12}\)
tag:模擬,遞歸
題解:每次要做的事情就是將一個平行四邊形切成邊長為短邊長度的若干等邊三角形,直到切不出來位置,切不出來的時候,就出現(xiàn)了一個新的平行四邊形,遞歸即可。
#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\) 個節(jié)點(diǎn)的樹,你可以刪除葉子,問最少刪除多少點(diǎn)之后可以使它的直徑小于等于 \(k\) \(n\le 1000\)
tag:圖論,貪心
題解:根據(jù) \(k\) 的奇偶性分類做,如果是偶數(shù),枚舉一個點(diǎn)作為直徑中點(diǎn),把距離這個點(diǎn)大于 \(\frac{k}{2}\)的點(diǎn)全部砍掉,如果是奇數(shù),枚舉一條邊, 把距離這條邊大于 \(\frac{k-1}{2}\) 的點(diǎn)砍掉,然后取 \(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\) 序列,請你重排它并構(gòu)造一個 \(b\) 序列,使得兩個序列元素和均為 \(n\) 。
對于一個長度為 \(n\) 的字符串,滿足如果前 \(a_1\) 個是回文串,接下來 \(a_2\) 個是回文串……且前 \(b_1\) 個是回文串,接下來 \(b_2\) 個是回文串……那么這個字符串處處相同。
tag:圖論,思維,貪心,構(gòu)造
題解:建立一個圖論模型。對于欽定相等的兩個點(diǎn),給它們連一條邊,然后一個聯(lián)通塊內(nèi)的所有點(diǎn)都要相等。那對于一個長度為 \(n\) 的回文串,其實(shí)就是欽定了 \(i\) 與 \(n-i+1\) 要相等,相當(dāng)于在圖中連了 \(\frac{n}{2}\) 條邊。為了讓 \(n\) 個點(diǎn)連通,我們需要至少 \(n-1\) 條邊。對于一個由若干段回文構(gòu)成的序列,它至多連出 \(\frac{n-odd}{2}\) 條邊,其中 \(odd\) 表示大小為奇數(shù)的段數(shù)。可以看出,若一開始給定的數(shù)列中,奇數(shù)大于 \(2\) 個,必然無解。那么對于一個有解的數(shù)列,把奇數(shù)放在首尾得到序列 \(1\) ,第一個數(shù)減一、最后一個數(shù)加一得到序列 \(2\) 。這樣,每一段都是錯開的,每一條邊都必然連接兩個原本不連通的塊。因?yàn)轭}目要求不能輸出 \(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\) 個數(shù)對 \((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:組合意義,計(jì)數(shù),dp
題解:神仙題,毫無思路 可能因?yàn)槲姨肆恕?/p>
考慮這個東西的組合意義,我們知道 \(\binom{x+y}{x}\) 可以表示成坐標(biāo)軸上 \((0,0)\) 走到 \((x,y)\) ,每次只能向右或上走的方案數(shù),所以問題變成了從 \((0,0)\) 走到 \((A_i+A_j,B_i+B_j)\) 的方案數(shù)。
我們平移一下這兩個點(diǎn),變成了從 \((-A_i,-B_i)\) 走到 \((A_j,B_j)\) 的方案數(shù),那么我們換一個思維,把所有的 \((-A_i,-B_i)\) 作為起點(diǎn),求出所有的 \((A_j,B_j)\) 到它的路徑條數(shù)。
那么我們考慮遞推,對于每一個 \(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)\) 的方案數(shù),多算了減掉
時間復(fù)雜度 \(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\) ,也是可以做的
我們觀察到第一象限的任何一個點(diǎn)到第三象限必定經(jīng)過 \(y=-x\) 這條直線,那么我們考慮枚舉經(jīng)過的是哪一個點(diǎn)
時間復(fù)雜度 \(O(\sum x_i+y_i)\)
Problem F Wide Swap
簡要題意:給定一個元素集合 \(\{1,2....N\}\) 的排列 \(P\) ,當(dāng)有 \(i,j\) 滿足 \(j-i\ge K\) 且 \(|P_i-P_j|=1\) 可以交換 \(P_i,P_j\)
求可能的字典序中最小的排列。\(N\le 5\times 10^5\)
tag:拓?fù)渑判?#xff0c;線段樹,貪心,思維
題解:依然毫無思路
首先考慮調(diào)換權(quán)值與下標(biāo)的位置,那么就可以把題目轉(zhuǎn)換成:相鄰元素且權(quán)值差 \(\ge k\) 的可以換順序,讓前面的權(quán)值盡量小的序列。
從位置 \(i\) 向后面的 \(j\) 比較,如果滿足條件 \(abs(a[i]-a[j])<k\) ,那么 \(i,j\) 的相對位置關(guān)系就確定了,可以連邊,這樣問題就變成了一個明顯的拓?fù)渑判颉?/p>
不過這樣連邊數(shù)是 \(N^2\) 級別的,需要優(yōu)化。
因?yàn)橐笄懊娴臋?quán)值盡可能小,所以連向的是最小的邊,所以從 \(a[i]\) 向 \((a[i]+1,a[i]+k-1)\) 中下標(biāo)最小的連邊就可以了,反過來,也要向 \((a[i]-k+1,a[i]-1)\) 中下標(biāo)最大的連邊,這樣可以包含所有情況,因?yàn)樗鼈兊南鄬ξ恢靡欢ㄒ部窟B邊確定了。
這樣,我們把邊數(shù)優(yōu)化到了 \(O(n)\) 級別,算法時間復(fù)雜度 \(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; }轉(zhuǎn)載于:https://www.cnblogs.com/crazyzh/p/10883746.html
總結(jié)
以上是生活随笔為你收集整理的AGC001 补题小结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信成语小秀才第512关答案
- 下一篇: iPhone 屏幕会在哪些情况下失灵