整天挨着毛爷爷,压力好大。。
看毛爷爷即将炖完NOI,我的确也该刷了
原则是从头到尾自己想(虽然看了一次题解),可以不A掉。
NOI2009
day1:
T1
题目略神,我还是不讲了。。。(就这题我WA了好多遍 TAT)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <bitset> 5 #include <vector> 6 7 #define N 20010 8 9 using namespace std; 10 11 struct edge{ 12 int x,to; 13 }E[N<<2]; 14 15 int n,a[N],pre[N],totE,g[N]; 16 bitset<N> v; 17 vector<int> G[N]; 18 19 #define p G[x][i] 20 21 void ade(int x,int y){ 22 G[x].push_back(y); 23 } 24 25 int find(int x){ 26 v[x]=1; 27 int len=G[x].size(); 28 for(int i=0;i<len;i++) 29 if(!v[p]){ 30 v[p]=1; 31 if(!pre[p]||find(pre[p])){ 32 pre[p]=x; pre[x]=p; 33 return 1; 34 } 35 } 36 return 0; 37 } 38 39 int main(){ 40 freopen("transform.in","r",stdin); 41 freopen("transform.out","w",stdout); 42 scanf("%d",&n); 43 for(int i=1;i<=n;i++){ 44 scanf("%d",&a[i]); 45 } 46 for(int i=1;i<=n;i++){ 47 if(i-a[i]>=1) G[i].push_back(n+i-a[i]); 48 else G[i].push_back(n+i-a[i]+n); 49 if(i+a[i]<=n) G[i].push_back(n+i+a[i]); 50 else G[i].push_back(i+a[i]); 51 } 52 for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end()); 53 int ans=0; 54 for(int i=n;i>=1;i--){ 55 v.reset(); 56 if(find(i)) ans++; 57 } 58 if(ans!=n){ 59 puts("No Answer"); 60 } 61 else{ 62 for(int i=1;i<n;i++) printf("%d ",pre[i]-n-1); 63 printf("%d\n",pre[n]-n-1); 64 } 65 return 0; 66 }
T2
脑补一下,这个应该是一个单峰函数,果断三分。
爆long long QAQ,这要是NOI我就被坑死啦,long double过。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 #define LL long long 6 #define N 200010 7 #define LD long double 8 9 using namespace std; 10 11 int n,P; 12 int len[N],pre[N]; 13 LL L; 14 LD f[N]; 15 char S[N][52]; 16 17 LD qpow(LD x,LL n){ 18 LD ans=1; 19 for(;n;n>>=1,x*=x) 20 if(n&1) ans*=x; 21 return ans; 22 } 23 24 LD Abs(LL x){ 25 if(x<0) return -(LD)x; 26 return (LD)x; 27 } 28 29 LD F(int i,int j){ 30 return f[j]+ 31 qpow(Abs(L-(len[i]-len[j]+i-j-1LL)),P); 32 } 33 34 int find(int i){ 35 int l=0,r=i-1,m1,m2; 36 while(r-l>10){ 37 m1=l+(r-l)/3; 38 m2=r-(r-l)/3; 39 if(F(i,m1)<F(i,m2)) r=m2; 40 else l=m1; 41 } 42 LD ans=F(i,0); 43 int pos=0; 44 for(int t=l;t<=r;t++) 45 if(F(i,t)<ans){ 46 ans=F(i,t); 47 pos=t; 48 } 49 return pos; 50 } 51 52 void print(int t){ 53 if(pre[t]) print(pre[t]); 54 for(int i=pre[t]+1;i<t;i++){ 55 printf("%s ",S[i]); 56 } 57 printf("%s\n",S[t]); 58 } 59 60 int main(){ 61 freopen("noi09_poet.in","r",stdin); 62 freopen("noi09_poet.out","w",stdout); 63 int T; 64 scanf("%d",&T); 65 while(T--){ 66 scanf("%d %lld %d",&n,&L,&P); 67 for(int i=1;i<=n;i++){ 68 scanf("%s",S[i]); 69 len[i]=strlen(S[i]); 70 } 71 for(int i=2;i<=n;i++) len[i]+=len[i-1]; 72 memset(f,127,sizeof(f)); 73 f[0]=0; 74 for(int i=1;i<=n;i++){ 75 int j=find(i); 76 f[i]=f[j]+qpow(Abs(L-(len[i]-len[j]+i-j-1LL)),P); 77 pre[i]=j; 78 } 79 if(f[n]>1e18){ 80 puts("Too hard to arrange"); 81 } 82 else{ 83 printf("%lld\n",(LL)(f[n])); 84 print(n); 85 } 86 puts("--------------------"); 87 } 88 return 0; 89 }
T3
QAQ好难,膜毛爷爷。
首先所谓 (频度 x 深度) 我们可以每过一层就加上当前的频度得到答案。
利用先序遍历的特殊性质(TAT 其实想到这个就差不多了),考虑dp。
f[i][j][k] 表示先序遍历从 i 到 j 作为一个子树(可能多个),而且满足所有权值小于等于k的最小花费。
转移分两种情况,修改当前root权值,修改子树权值。
$O(n^4)$ dp即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 #define N 80 7 #define LL long long 8 #define INF 0x7f7f7f7f7f7f7f7fLL 9 #define debug(x) cout<<#x<<" = "<<x<<endl; 10 11 using namespace std; 12 13 int n,tot0; 14 int a[N],b[N],c[N],X[N]; 15 LL K; 16 LL dp[N][N][N]; 17 LL s[N][N]; 18 19 /* 20 O(n^5) 21 22 f[i][j][k] 23 i到j之间的所有数值大于k, 24 消灭当前点 f[i][j][k] = min{ f[i][p-1][k] + f[p+1][j][k] + s[i][j] + K } 25 不消灭当前点 f[i][j][k] = min{ f[i][p-1][b[p]] + f[p+1][j][b[p]] + s[i][j] } 26 */ 27 28 LL F(int i,int j,int k){ 29 if(i>j) return 0; 30 // printf("%d %d %d\n",i,j,k); 31 if(dp[i][j][k]) return dp[i][j][k]; 32 if(i==j) return dp[i][j][k]=b[i]<k ?K+c[i]:c[i]; 33 dp[i][j][k]=INF; 34 for(int p=i;p<=j;p++){ //改当前点权 35 dp[i][j][k]=min(dp[i][j][k],F(i,p-1,k) + F(p+1,j,k) + s[i][j] + K); 36 if(b[p]>=k){ //不改当前点权 37 dp[i][j][k]=min(dp[i][j][k],F(i,p-1,b[p]) + F(p+1,j,b[p]) + s[i][j]); 38 } 39 } 40 return dp[i][j][k]; 41 } 42 43 void init(){ 44 for(int i=1;i<n;i++) 45 for(int j=i+1;j<=n;j++) 46 if(a[i]>a[j]){ 47 swap(a[i],a[j]); 48 swap(b[i],b[j]); 49 swap(c[i],c[j]); 50 } 51 sort(X+1,X+n+1); 52 tot0=1; 53 for(int i=2;i<=n;i++) 54 if(X[i]!=X[i-1]) X[++tot0]=X[i]; 55 for(int i=1;i<=n;i++) 56 b[i]=lower_bound(X+1,X+tot0+1,b[i])-X; 57 for(int i=1;i<=n;i++){ 58 s[i][i]=c[i]; 59 for(int j=i+1;j<=n;j++) 60 s[i][j]=s[i][j-1]+c[j]; 61 } 62 } 63 64 int main(){ 65 freopen("treapmod.in","r",stdin); 66 freopen("treapmod.out","w",stdout); 67 scanf("%d%d",&n,&K); 68 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 69 for(int i=1;i<=n;i++){ 70 scanf("%d",&b[i]); 71 X[i]=b[i]; 72 } 73 for(int i=1;i<=n;i++) scanf("%d",&c[i]); 74 init(); 75 LL ans=INF; 76 for(int i=1;i<=n;i++){ 77 ans=min(ans,F(1,n,b[i])); 78 } 79 cout<<ans<<endl; 80 return 0; 81 }
day2
T1:
裸网络流,注意要拓扑一下,还要判环,环内的植物不能碰。
T2:
又是一道$dp$,思路很神(TAT 实在不会,看了题解)。
就是将 平方和 转化为结果相同的方案对数(自己也算)
然后裸dp上。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 #define N 510 6 #define mod 1024523 7 8 using namespace std; 9 10 /* 11 f[i][j][k]表示上面取了上i下j,上k。 12 */ 13 14 char S1[N],S2[N]; 15 int f[N][N][N],n,m; 16 17 int add(int a,int b){ 18 if(a+b<mod) return a+b; 19 return a+b-mod; 20 } 21 22 int main(){ 23 freopen("ballb.in","r",stdin); 24 freopen("ballb.out","w",stdout); 25 scanf("%d%d%s%s",&n,&m,S1,S2); 26 reverse(S1,S1+n); 27 reverse(S2,S2+m); 28 f[0][0][0]=1; 29 for(int i=0;i<=n;i++){ 30 for(int j=0;j<=m;j++){ 31 for(int k=0;k<=n;k++){ 32 if(!f[i][j][k]) continue; 33 int l = i+j-k; 34 if(l<0) break; 35 //printf("f[%d][%d][%d] = %d\n",i,j,k,f[i][j][k]); 36 if(i<n && k<n && S1[i]==S1[k]){ 37 f[i+1][j][k+1] = add(f[i+1][j][k+1],f[i][j][k]); 38 } 39 if(i<n && l<m && S1[i]==S2[l]){ 40 f[i+1][j][k] = add(f[i+1][j][k],f[i][j][k]); 41 } 42 if(j<m && k<n && S2[j]==S1[k]){ 43 f[i][j+1][k+1] = add(f[i][j+1][k+1],f[i][j][k]); 44 } 45 if(j<m && l<m && S2[j]==S2[l]){ 46 f[i][j+1][k] = add(f[i][j+1][k],f[i][j][k]); 47 } 48 } 49 } 50 } 51 printf("%d\n",f[n][m][n]); 52 return 0; 53 }
T3:
很好的提交答案题。
做好了70分吧。
时间: 2024-10-13 15:59:35