題意:1表明圓,2表明正三角形,3表明正方形。給一個只含1,2,3的數列a,ai+1內接在ai內,求總共有多少個交點。算法
交了好多遍才過。分類討論一下內接的狀況,而後注意到當正方形內接圓形再內接三角形時會有一個點重複,減去便可。優化
codespa
#include<cstdio> int n,fig[105],ans,flag,haha[5][5]; int main() { scanf("%d",&n); haha[1][2]=3;haha[1][3]=4; haha[2][1]=3;haha[2][3]=999999; haha[3][1]=4;haha[3][2]=999999; for(int i=1;i<=n;i++) { scanf("%d",&fig[i]); ans+=haha[fig[i-1]][fig[i]]; if(fig[i]==2&&fig[i-1]==1&&fig[i-2]==3)ans--; if(ans>=999999) { printf("Infinite\n"); return 0; } } printf("Finite\n"); printf("%d",ans); }
題意:給一個字符串,要求從新排列後不能有字母表中相鄰的字母被放在一塊兒(如「ab」與「ba」就不合法)。若能夠作到就輸出字符串,反之輸出「No answer」。code
相同的字母能夠縮成一個。發現只有當給的字母是兩個或三個相鄰的字母時是沒法作到的(「ab」,「abc」),特判處理。對全部的字母排序後就能夠亂搞了。blog
當總共有奇數個字母時,將a(1+n)/2拿出來放到答案中的第一個,剩下的就按a1,an,a2,an-1......這樣來排。排序
(總共有3個字母時可能第一個或最後一個與中間的那個相鄰,特判處理一下)隊列
當總共有偶數個字母時,將a(1+n)/2+1放到答案中的第一個,a1放到答案中的第二個,an放到答案的第三個,a(1+n)/2放到答案的第四個,剩下的就按an-1,a2,an-2,a3....這樣來排。遊戲
(一開始還想用哈密頓迴路來作)字符串
codestring
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int T,cha[26],tot,tim[26],ans[26]; char ch[105]; int main() { scanf("%d",&T); while(T--) { scanf("%s",ch+1); int len=strlen(ch+1); for(int i=1;i<=len;i++) { if(!tim[ch[i]-'a']) cha[++tot]=ch[i]-'a'; tim[ch[i]-'a']++; } sort(cha+1,cha+1+tot); if((tot==3&&cha[1]+1==cha[2]&&cha[2]+1==cha[3])||(tot==2&&cha[1]+1==cha[2])) printf("No answer\n"); else { if(tot%2==0) { int mid1=tot/2,mid2=mid1+1; ans[1]=cha[mid2];ans[2]=cha[1]; ans[3]=cha[tot];ans[4]=cha[mid1]; int l=4; for(int i=2;i<mid1;i++) ans[++l]=cha[tot-i+1],ans[++l]=cha[i]; } else { ans[1]=cha[tot/2+1]; if(tot!=3||(tot==3&&cha[1]+1!=cha[2]&&cha[2]+1!=cha[3])) for(int i=1;i<=tot/2;i++) ans[i*2]=cha[i],ans[i*2+1]=cha[tot-i+1]; else { if(cha[1]+1==cha[2]) ans[2]=cha[3],ans[3]=cha[1]; else ans[2]=cha[1],ans[3]=cha[3]; } } for(int i=1;i<=tot;i++) for(int j=1;j<=tim[ans[i]];j++) printf("%c",ans[i]+'a'); printf("\n"); } memset(cha,0,sizeof cha);tot=0; memset(ch,0,sizeof ch); memset(tim,0,sizeof tim); } return 0; }
題意:給一串數列a與數字z,若|ai-aj|>=z,則ai與aj能夠匹配。一個數只能匹配一次,求最大匹配數。
本能地給數列排序後,發現新數列有這樣一個性質:如有兩個匹配之間跨越範圍不相交,那麼咱們可讓它們變成相交的,這樣不會影響它們對答案的貢獻。但這個性質反過來不必定成立,因此咱們匹配時最好讓它們相交。
把數列的中點取出,把中點右邊的點當作匹配的右端點,左邊的當作左端點,依次匹配就行了。這樣作可讓全部匹配跨越範圍都通過中點,即在中點相交。
論證一下嚴謹性。若是有一邊的點能造成不過中點的匹配,那麼它確定能經過另外一邊的某個點變爲過中點的匹配(由於取的是中點,兩邊點的個數最多相差1,因此另外一邊確定存在一個點能保證造成新的匹配)。
code
#include<cstdio> #include<algorithm> using namespace std; int n,z,num[200005],ans,vis[200005]; int main() { scanf("%d%d",&n,&z); for(int i=1;i<=n;i++) scanf("%d",&num[i]); sort(num+1,num+1+n); for(int i=(1+n)/2+1,j=1;i<=n;i++) { while(vis[j])j++; if(!vis[i]&&!vis[j]&&num[i]-num[j]>=z) vis[i]=vis[j]=1,ans++,j++; } printf("%d\n",ans); }
題意:給一棵樹,邊的權值只有0和1。數對(x,y),x到y的路徑上通過權值爲0的邊後就再也沒有通過權值爲1的邊,求知足條件的數對的個數。
用並查集求出全部邊權只含0和1的連通塊,設每一個連通塊的點的個數爲siz。而後分類討論,只含權值爲1或0的邊的路徑,則每一個連通塊的貢獻爲siz*(siz-1)。含權值爲0和權值爲1的邊的路徑,枚舉路徑上01轉變的點,找到包含它的只含0的連通塊i與只含1的連通塊j,產生的貢獻爲(sizi-1)*(sizj-1)。
(不須要擔憂兩個聯通塊還有其它公共點,由於這是棵樹)
看到zzwy大佬還有一種巧妙的算法,直接枚舉1.0轉變的點,找到包含它的只含0的連通塊i與只含1的連通塊j,產生的貢獻爲sizi*sizj-1,這樣還同時枚舉了邊權只含0或1的狀況,減去1是去掉本身到本身。
code
#include<cstdio> int ori[200005][5],n,tim[200005][2]; long long ans=0; int find(int x,int k){return !ori[x][k]?x:ori[x][k]=find(ori[x][k],k);} int main() { scanf("%d",&n); for(int i=1,a,b,c;i<=n-1;i++) { scanf("%d%d%d",&a,&b,&c); int t1=find(a,c),t2=find(b,c); if(t1!=t2) ori[t1][c]=t2; } for(int i=1;i<=n;i++) tim[find(i,1)][1]++,tim[find(i,0)][0]++; for(int i=1;i<=n;i++) ans+=1ll*(tim[find(i,1)][1])*(tim[find(i,0)][0])-1; printf("%lld\n",ans); }
題意:給一個數列p,求全部的數對(l,r)的個數,知足pl+pr=maxri=lpi。
這題抄的題解和代碼:
對於每一個數a,咱們能夠用單調隊列求出它左邊第一個和右邊第一個比它大的數al與ar,那麼中間的這段區間(l,r)它就是最大值,以後就暴力枚舉就行了。經過相似啓發式合併的思想(反正我還沒學)能夠證實複雜度爲nlogn。
code
#include<cstdio> #define maxn 200005 int q[maxn],be=1,en,n,p[maxn],pos[maxn],L[maxn],R[maxn]; long long ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&p[i]),pos[p[i]]=i;p[n+1]=200005;p[0]=200005; for(int i=0;i<=n+1;i++) {for(;be<=en&&p[q[en]]<p[i];en--);L[i]=q[en];q[++en]=i;} for(int i=n+1;i>=0;i--) {for(;be<=en&&p[q[en]]<p[i];en--);R[i]=q[en];q[++en]=i;} for(int i=1;i<=n;i++) { if(i-L[i]<R[i]-i) for(int j=L[i]+1;j<i;j++) { if(R[i]>pos[p[i]-p[j]]&&pos[p[i]-p[j]]>i)ans++;} else for(int j=R[i]-1;i<j;j--) { if(L[i]<pos[p[i]-p[j]]&&pos[p[i]-p[j]]<i)ans++;} } printf("%I64d\n",ans); }
題意:給一堆牌,隨便拿,若此次的值小於上次則失敗,等於上次的值則成功,大於上次的值則繼續遊戲,求勝利的指望值。
第一道本身寫的機率dpQAQ
首先對於任何一個不失敗的狀態,摸到牌造成的序列應該是不降低的,因此先排個序。咱們能夠假設dp[i][j]表示第i張牌摸到了值爲j的牌且遊戲未結束的指望值。由於序列不降低且遊戲還沒有結束,那麼值爲j的牌就只用了一張。則dp[i][j]能夠由dp[i+1][k](k>j)轉移過來。首先,先預處理出每一個數字的個數cnt,而後就能夠進行轉移了。dp[i][j]的指望值應該是 (cntj-1)/n-i 加上(∑dp[i+1][k])/(n-i)(k>j),即下一次抽到j的指望值加上以後獲勝的指望值,再用前綴和優化一下就行了。
code
#include<cstdio> #include<algorithm> #define maxn 5005 #define mod 998244353 using namespace std; int n,a[maxn],inv[maxn],num[maxn],cnt[maxn],f[maxn][maxn],flag=1; int main() { scanf("%d",&n); inv[1]=1;for(int i=2;i<=n;i++)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=1;i<=n;cnt[a[i]]++,i++) if(!cnt[a[i]])num[++num[0]]=a[i]; sort(num+1,num+1+num[0]); for(int i=n;i>=1;i--) { long long sum=0; for(int k=1;k<=num[0];k++) sum=(sum+1ll*f[flag^1][k]*cnt[num[k]]%mod*inv[n-i]%mod)%mod; for(int j=1;j<=num[0];j++) { sum=(sum-1ll*f[flag^1][j]*cnt[num[j]]%mod*inv[n-i]%mod+mod)%mod; f[flag][j]=(1ll*(cnt[num[j]]-1)*inv[n-i]%mod+sum)%mod; } flag^=1; } int ans=0; for(int i=1;i<=num[0];i++) ans=(ans+1ll*f[flag^1][i]*cnt[num[i]]%mod*inv[n]%mod)%mod; printf("%d\n",ans); }
恕在下不肖,真的不會。