题源:
A:https://vjudge.net/problem/CodeForces-1287A
B:https://vjudge.net/problem/CodeForces-1287B
C:https://vjudge.net/problem/CodeForces-1287C
D:https://vjudge.net/problem/CodeForces-1287D
A - Angry Students
队友出的 没做 懒得补 应该是水题 略了
B - Hyperset
字符串的题 意思是有n个字符串 每个字符串有m个特征 每个特征都是’S’ ‘E’ ‘T’
让你从字符串中任取三个 需要满足:这三个字符串的每个特征 要么全相同(SSS或EEE或TTT) 要么全不同(三个各取SET其中之一)
问你能找出多少组这样的字符串。(n<=1500 m<=15
自己刚开始莽啊!直接暴力 自然t在第11组了
然后又按第一个字母分组了一下 稍微优化一下 又t在第51组
最后还是没做出来。
其实自己没想到的是,如果两个字符串确定,那剩下那个字符串一定是确定的。
所以只要用map存储一下最初的字符串 最后n^2遍历所有组合可能 再看看剩下那个是否拥有即可。当然这里默认给出的字符串不重复!不然查询时不应该++ 应该加上总个数!
下面是代码:(感谢lc大佬)
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
//这题思路应该是:
//首先如果两个确定 那第三个一定确定!
//所以n^2初始化当前所有种的可能性
//最后再On查询即可
//当然这题默认所给的每种子串不重复 不然还不行
int n,m;
string s[1510];
map<string,bool> mp;
char g[200][200];
void init(){
g['S']['E']=g['E']['S']='T';
g['S']['T']=g['T']['S']='E';
g['E']['T']=g['T']['E']='S';
g['S']['S']='S';
g['E']['E']='E';
g['T']['T']='T';
}
bool judge(int i,int j){
string tp="";
for(int p=0;p<m;p++){
tp+=g[s[i][p]][s[j][p]];
}
return mp[tp];
}
int main() {
ios::sync_with_stdio(false);
init();
cin>>n>>m;
rep(i,1,n) {
cin>>s[i];
mp[s[i]]=true;
}
ll cnt=0;
rep(i,1,n-1){
rep(j,i+1,n){
if(judge(i,j)) cnt++;
}
}
cout<<cnt/3<<endl;
return 0;
}
C - Garland
题意:给你一个序列 n个数 只有1~n这些值
有0空着 可以放剩下的数
问你最少有几个奇偶对挨着
自己思路:(贪心吧应该是
因为如果两个偶数间放个奇数或者两个奇数之间放个偶数 那显然会+2
如果在开头或末尾加上不同的数 会+1
所以优先满足两奇数或两偶数之间的0
所以排序所有偶偶区间和奇奇区间 从短到长
然后去满足
如果不够了 再满足开头和末尾
不用管奇偶区间 因为必然有一个!
自己刚开始没有考虑 特判n==1 应该输出0
加上就对了
然后先是dp算法 再是自己的方法
dp方法:
感觉很强 自己dp一直不好。。。
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
//这题用dp dp[i][j][2] 代表当前位置为i j个偶数且当前位为1奇/0偶时 最小的复杂度为多少
const int maxn=110;
int n;
int a[maxn];
int dp[maxn][maxn][2];
#define min(x,y) ((x)<(y)?(x):(y))
int main() {
scanf("%d",&n);
rep(i,1,n) scanf("%d",&a[i]);
cl(dp,inf);
dp[0][0][1]=dp[0][0][0]=0;
rep(i,1,n){//所有长度
rep(j,0,i/2){//当前长度 所有已有长度的可能
if(a[i]==0||a[i]%2==0){
dp[i][j+1][0]=min(dp[i][j+1][0],dp[i-1][j][0]);
dp[i][j+1][0]=min(dp[i][j+1][0],dp[i-1][j][1]+1);
}
if(a[i]==0||a[i]%2){
dp[i][j][1]=min(dp[i][j][1],dp[i-1][j][1]);
dp[i][j][1]=min(dp[i][j][1],dp[i-1][j][0]+1);
}
}
}
printf("%d\n",min(dp[n][n/2][0],dp[n][n/2][1]));
return 0;
}
下面是自己的模拟方法
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
struct node{
int l,r;
}q1[110],q0[110];
bool cmp(node a,node b){
return (a.r-a.l)<(b.r-b.l);
}
int main() {
int n;
int num[110];
cl(num,0);
scanf("%d",&n);
int cnt1=0,cnt0=0;
rep(i,1,n){
scanf("%d",&num[i]);
if(num[i]==0) continue;
if(num[i]&1) cnt1++;
else cnt0++;
}
if(n==1){
printf("0\n");
return 0;
}
if(cnt1==0&&cnt0==0){
printf("1\n");
return 0;
}
if(n&1){
cnt1=n/2+1-cnt1;
cnt0=n/2-cnt0;
}else{
cnt1=n/2-cnt1;
cnt0=n/2-cnt0;
}
int k1=0,k0=0;//记录多少偶偶和奇奇区间了
int s=1,t=n;
while(num[s]==0&&s<=n) ++s;
while(num[t]==0&&t>=1) --t;
int x=0;//这个统计有多少不相同的
for(int i=s;i<t;){
if(num[i]!=0){
if(num[i]&1){
for(int j=i+1;j<=t;j++){
if(num[j]==0) continue;
else {
if(num[j]&1){
if(j-i>1){
k1++;
q1[k1].l=i;
q1[k1].r=j;
}
i=j;
break;
}else {//不相同的
x++;
i=j;
break;
}
}
}
}else{
for(int j=i+1;j<=t;j++){
if(num[j]==0) continue;
else {
if((num[j]&1)==0){
if(j-i>1){
k0++;
q0[k0].l=i;
q0[k0].r=j;
}
i=j;
break;
}else {
x++;
i=j;
break;
}
}
}
}
}else i++;
}
if(k1) sort(q1+1,q1+k1+1,cmp);
if(k0) sort(q0+1,q0+k0+1,cmp);
//printf("%d %d\n",cnt1,cnt0);
int i;
//相同奇之间
for(i=1;i<=k1;i++){
if(cnt1>=q1[i].r-q1[i].l-1){
cnt1-=(q1[i].r-q1[i].l-1);
}else break;
}
//若不能填补了
if(i<=k1) x+=2*(k1-i+1);//每当有一组
//再看相同偶之间
for(i=1;i<=k0;i++){
if(cnt0>=q0[i].r-q0[i].l-1){
cnt0-=(q0[i].r-q0[i].l-1);
}else break;
}
if(i<=k0) x+=2*(k0-i+1);
//看开头
if(s!=1){
if(num[s]&1) {
if(cnt1>=(s-1)) cnt1-=(s-1);
else x++;
}else {
if(cnt0>=(s-1)) cnt0-=(s-1);
else x++;
}
}
//看结尾
if(t!=n){
if(num[t]&1){
if(cnt1>=(n-t)) cnt1-=(n-t);
else x++;
}else{
if(cnt0>=(n-t)) cnt0-=(n-t);
else x++;
}
}
printf("%d\n",x);
return 0;
}
D - Numbers on Tree
题意:给出一棵有根树,每个节点都有一个权值,代表的是在其子树中有多少个节点的val比他小,现在要求根据每个点的权值构造出1~n的val数列
思路:先满足父节点 因为只有c个比他小 那他就是c+1 然后用过的数要标记一下
对于每一个子树都这样递归做
答案不唯一!
1~n一定可以表示完所有的
而且某一个结点的子树结点数量一定要大于等于这个结点的c 不然直接输出no!
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
//这里可以用题解这个方法 主要原因是 深搜!
//因为搜了某一结点之后 会继续搜它的孩子结点
//所以说 a值小的 不会被兄弟的叶子节点所占领
const int maxn=2010;
int n,cnt;
struct Side{
int v,next;
}side[maxn];
int head[maxn],c[maxn],a[maxn],num[maxn];//num是子节点
bool con[maxn];//con[i]意为 i这个值有没有被用过
void add(int u,int v){
side[cnt].v=v;
side[cnt].next=head[u];
head[u]=cnt++;
}
void dfs_son(int u){//获取儿子数目
//你瞎输出能对么!!!
num[u]=1;//必须是1
for(int i=head[u];i!=-1;i=side[i].next){
int v=side[i].v;
dfs_son(v);
num[u]+=num[v];
}
return;
}
void dfs(int u){//从根节点开始嘛 因为先满足根节点
int cnt=0;
for(int i=1;i<=n;i++){
if(!con[i]) cnt++;
if(cnt==c[u]+1){
a[u]=i;
break;//找到当前节点的即可
}
}
con[a[u]]=true;
for(int i=head[u];i!=-1;i=side[i].next) dfs(side[i].v);
}
void init(){
cl(head,-1);
cl(con,0);
cnt=0;
}
int main() {
init();
int root;
scanf("%d",&n);
rep(i,1,n){
int f,cc;
scanf("%d%d",&f,&cc);
if(f!=0) add(f,i);
else root=i;
c[i]=cc;
}
dfs_son(root);
rep(i,1,n){
if(num[i]<c[i]+1){
printf("NO\n");
return 0;
}
}
dfs(root);
printf("YES\n");
rep(i,1,n){
printf("%d ",a[i]);
}
return 0;
}