莫名每道题文件忘记加.in .out???
修正之后分数:10+100+0=110(太菜了...)
T1序列分解
题目描述:
老胡有一个长度为n(n为偶数)的序列a,现在他要把这个序列分解成两个长度为n/2的子序列,并满足如下要求:
1.两个子序列中的数在原序列中不能重叠。(即每个数要么在第一个子序列中,要么在第二个子序列中。)
2.两个子序列要完全一样。
然而这个问题对于老胡来说太简单了,所以他想请你来帮忙。
输入:
第一行给出一个T,表示T组数据。
对于每组数据,输入共2行。
第一行包含一个整数n。
第二行给出n个整数表示老胡的序列。
输出:
如果你完成了老胡的任务,他会说"Good job!!" (不包含引号),否则他会说"What a pity!" (不包含引号)。
样例输入:
2
4
1 1 2 2
6
1 2 3 4 5 6
样例输出:
Good job!!
What a pity!
数据规模:
50%: n<=20
100%: 1<=T<=5,2<=n<=40,-1,000,000,000<=a[i]<=1,000,000,000
考试的时候根本没看到子序列,应该是按顺序而不一定连续的...然后开了一个map开心的过了样例......(竟然还有分...)
正解:
n很小,考虑搜索。
按顺序搜,考虑第stp个数属于第1个数列还是第2个数列。复杂度O(2^n),爆炸
考虑剪枝
1、每个数列的数小于等于总长度一半(睿(ruo)智的剪枝)
2、如果第一个数列有l1个数,第二个数列有l2个数,若l1<l2且a[stp]==a2[l2]则必填入第一个,反之第二个同理
则代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<map> 6 using namespace std; 7 int T,n,a[455],flag; 8 inline int read(){ 9 int ans=0,f=1;char chr=getchar(); 10 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 11 while(isdigit(chr)) {ans=(ans<<1)+(ans<<3)+chr-48;chr=getchar();} 12 return ans*f; 13 } 14 inline void kai(){ 15 freopen("split.in","r",stdin); 16 freopen("split.out","w",stdout); 17 } 18 int s1[54],s2[54],l1,l2; 19 bool dfs(int stp){ 20 if(stp>n) return 1; 21 if(l1<=(n>>1)&&(l1<l2&&a[stp]==s2[l1]||l1>=l2)){ 22 s1[l1++]=a[stp]; if(dfs(stp+1)) return 1; --l1; 23 } 24 if(l2<=(n>>1)&&(l1>l2&&a[stp]==s1[l2]||l2>=l1)){ 25 s2[l2++]=a[stp]; if(dfs(stp+1)) return 1; --l2; 26 }return 0; 27 } 28 int main(){ 29 // kai(); 30 T=read(); 31 while(T--){ 32 n=read(); 33 for(int i=1;i<=n;i++) a[i]=read(); 34 l1=l2=1; 35 if(dfs(1)) puts("Good job!!");else puts("What a pity!"); 36 } 37 return 0; 38 }
T2:010串
题目描述:
如果一个01字符串满足不存在010这样的子串,那么称它为非010串。
求长度为n的非010串的个数。(对1e9+7取模)
输入:
一个数n,表示长度。
输出:
长度为n的非010串的个数。(对1e9+7取模)。
样例输入:
3
样例输出:
7
(解释):
000
001
011
100
101
110
111
数据规模:
60%: n<=1e7
100%: n<=1e15。
考试的时候:这种题肯定找规律啊,想递推式.......10min,放弃,打了个爆搜,弄出了前33个数的规律,然后做差,再做差,再做差……不知怎的就出来一个规律:
f[n]=f[n-1]+f[n-2]+f[n-4]
考完后正确的思路:
虽然两个递推式不一样,但是:把f[n-1]用 2f[n-2]-f[n-3]+f[n-4]代入,发现两式一样,嘿嘿
然后要一个矩阵加速,不过当时果断先拿了60分,看了一会儿T3,算了我还是先回来做T2吧...
然后打矩阵乘法......半天没过...忘记memset初始化矩阵,然而查了好久好久(半途打不出来还先去暴力了一下T3)然后好不容易所有数据都对上了一个矩乘打+查错用了一个小时...还是太弱了...矩乘不熟啊...不过最后还是A掉了
代码
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define int long long 7 #define ll long long 8 using namespace std; 9 const ll MOD=1e9+7; 10 inline int read(){ 11 int ans=0,f=1;char chr=getchar(); 12 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 13 while(isdigit(chr)) {ans=(ans<<1)+(ans<<3)+chr-48;chr=getchar();} 14 return ans*f; 15 } 16 inline void kai(){ 17 freopen("string.in","r",stdin); 18 freopen("string.out","w",stdout); 19 } 20 struct P{int m[10][10];}aa,b,p; 21 inline void First(){memset(aa.m,0,sizeof(aa.m));aa.m[0][0]=1;aa.m[0][2]=1;aa.m[1][0]=1;aa.m[1][2]=1;aa.m[2][3]=1;aa.m[3][1]=1;aa.m[3][3]=1;} 22 P E(){P e;for(int i=0;i<4;i++){for(int j=0;j<4;j++){if(i==j)e.m[i][j]=1;else e.m[i][j]=0;}}return e;} 23 P ksm(P x,int m){ 24 P c=E(); 25 while(m){ 26 if(m&1){ 27 P dp; 28 memset(dp.m,0,sizeof(dp.m)); 29 for(int i=0;i<4;i++) 30 for(int j=0;j<4;j++) 31 for(int k=0;k<4;k++) 32 dp.m[i][j]=dp.m[i][j]+x.m[i][k]*c.m[k][j]%MOD,dp.m[i][j]%=MOD; 33 c=dp; 34 } 35 P aa; 36 memset(aa.m,0,sizeof(aa.m)); 37 for(int i=0;i<4;i++) 38 for(int j=0;j<4;j++) 39 for(int k=0;k<4;k++) 40 aa.m[i][j]=aa.m[i][j]+x.m[i][k]*x.m[k][j]%MOD,aa.m[i][j]%=MOD; 41 x=aa; 42 m>>=1; 43 } 44 return c; 45 } 46 signed main(){ 47 kai(); 48 int n=read(); 49 if(n==1) cout<<2; 50 else if(n==2) cout<<4; 51 else{ 52 n-=2; 53 First(); 54 P aaa=ksm(aa,n); 55 int s=0; 56 for(int i=0;i<4;i++) 57 for(int j=0;j<4;j++) 58 s+=aaa.m[i][j],s%=MOD; 59 cout<<s; 60 } 61 return 0; 62 }
T3:最小生成树
题目描述:
给定一个长度为n的数组a[1..n],有一幅完全图,满足(u,v)的边权为a[u] xor a[v]。
求边权和最小的生成树,你需要输出边权和还有方案数对1e9+7取模的值。(注意边权和是不需要取模的)
输入:
第一行一个正整数n。
第二行n个整数表示a[1..n]。
输出:
第一行输出边权和。
第二行输出方案数。
样例输入:
5
2 2 3 4 5
样例输出:
8
6
数据规模:
50%: n<=1000
100%: 1<=n<=10^5,0<=a[i]<2^30。
思路:Kruskal的思想,江边从小到大插入,但是直接裸的话,就炸了
考虑优化:异或很有意思,考虑分每一位考虑,要是异或值最小,相同的位越多越好,考虑Trie字典树,相同部分越多,异或值越小,可以递归处理,直到接下来集合相同,然后用某个叫Pruffer的东西:某个完全图(n个节点)的生成树个数:n^(n-2).也就是说到集合相同之后(异或值肯定都是0)直接知道答案--->推出相同树的个数。代码有点难实现,涉及到多个算法的叠加...
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define int long long 6 using namespace std; 7 inline int read(){ 8 char chr=getchar(); int f=1,ans=0; 9 while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();} 10 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();} 11 return ans*f; 12 }void write(int x){ 13 if(x<0) putchar('-'),x=-x; 14 if(x>9) write(x/10); 15 putchar(x%10+'0'); 16 }const int M = 1e5+5,MOD=1e9+7; 17 int n,a[M],ans1,ans2; 18 int ksm(int x,int y){int ans=1;for(;y;y>>=1,x=1LL*x*x%MOD)if(y&1)ans=1LL*ans*x%MOD;return ans;}//快速幂 19 int nxt[M*30][2],tot[M*30],depth[M*30],flag[M*30],cnt=1; 20 inline void Insert(int v){//插入Trie 21 int x=1,t;//x->jump 22 for (int i=29;i>=0;i--){ 23 t=(v>>i)&1;//取第i位(从高位开始取) 24 if(!nxt[x][t])nxt[x][t]=++cnt; 25 x=nxt[x][t]; 26 depth[x]=i,flag[x]=t; 27 }tot[x]++; 28 }int minn,situ;//最小差值 29 void Min_Cost_Merge(int x,int y,int dif){ 30 dif|=(flag[x]^flag[y])<<depth[x]; 31 int f=0; 32 for(int k=0;k<2;k++){ 33 for(int t=0;t<2;t++) 34 if (nxt[x][t]>0&&nxt[y][t^k]>0) Min_Cost_Merge(nxt[x][t],nxt[y][t^k],dif),f=1; 35 if(f)return; 36 } 37 if (dif<minn)minn=dif,situ=0; 38 if (dif==minn)situ=(1LL*tot[x]*tot[y]+situ)%MOD; 39 }int solve(int x){ 40 if(x==0)return 0; 41 int s=solve(nxt[x][0])+solve(nxt[x][1]); 42 if(s==0&&tot[x]>1)ans2=1LL*ans2*ksm(tot[x],tot[x]-2)%MOD;//如果全是0,pruffer-->ans=n^(n-2); 43 if(s==2){ 44 minn=1<<30,situ=1; 45 Min_Cost_Merge(nxt[x][0],nxt[x][1],0);//Merge; 46 ans1+=minn,ans2=1LL*ans2*situ%MOD; 47 }return 1; 48 } 49 signed main(){ 50 n=read();ans2=1; 51 for(int i=1;i<=n;i++) a[i]=read(),Insert(a[i]); 52 solve(1);cout<<ans1<<endl<<ans2<<endl; 53 return 0; 54 }